Add Service overview and fixes for Statusdat

The service overview required a few fixes for issues that
occured because the StatusDat Query class now inherits from
Data/AbstractQuery.

refs #4178
This commit is contained in:
Jannis Moßhammer 2013-07-19 17:45:51 +02:00
parent 340554a58c
commit 5e4adcfea2
23 changed files with 809 additions and 264 deletions

View File

@ -0,0 +1,71 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat;
/**
* Class ObjectContainer
* @package Icinga\Protocol\Statusdat
*/
class ObjectContainer extends \stdClass
{
/**
* @var \stdClass
*/
public $ref;
/**
* @var IReader
*/
public $reader;
/**
* @param \stdClass $obj
* @param IReader $reader
*/
public function __construct(\stdClass &$obj, IReader &$reader)
{
$this->ref = & $obj;
$this->reader = & $reader;
}
/**
* @param $attribute
* @return \stdClass
*/
public function __get($attribute)
{
$exploded = explode(".", $attribute);
$result = $this->ref;
foreach ($exploded as $elem) {
$result = $result->$elem;
}
return $result;
}
}

View File

@ -49,10 +49,10 @@ class Query extends AbstractQuery
"servicegroups" => array("servicegroup"),
"comments" => array("servicecomment", "hostcomment"),
"hostcomments" => array("hostcomment"),
"servicecomments" => array("servicecomment"),
"status" => array("host", "service")
"servicecomments" => array("servicecomment")
);
/**
* @var IReader|null
*/
@ -63,11 +63,6 @@ class Query extends AbstractQuery
*/
private $source = "";
/**
* @var array
*/
protected $columns = array();
/**
* @var null
*/
@ -165,13 +160,7 @@ class Query extends AbstractQuery
return $this->offset;
}
/**
* @param IReader $reader
*/
public function __construct(IReader $reader)
{
$this->ds = $reader;
}
/**
* @param $key
@ -248,7 +237,6 @@ class Query extends AbstractQuery
} else {
throw new \Exception("Unknown from target for status.dat :" . $table);
}
$this->columns = $columns;
return $this;
}
@ -269,7 +257,8 @@ class Query extends AbstractQuery
$state = $this->ds->getObjects();
$result = array();
foreach (self::$VALID_TARGETS[$this->source] as $target) {
$source = self::$VALID_TARGETS[$this->source];
foreach ($source as $target) {
$indexes = & array_keys($state[$target]);
if ($baseGroup) {
$indexes = & $baseGroup->filter($state[$target]);
@ -307,7 +296,6 @@ class Query extends AbstractQuery
$o2 = & $this->ds->getObjectByName($this->currentType, $b);
$result = 0;
foreach ($this->order_columns as $col) {
$result += $col[1] * strnatcasecmp($o1->{$col[0]}, $o2->{$col[0]});
}
if ($result > 0) {
@ -410,4 +398,5 @@ class Query extends AbstractQuery
}
return $result;
}
}

View File

@ -29,6 +29,7 @@
namespace Icinga\Protocol\Statusdat;
use Icinga\Data\DatasourceInterface;
use Icinga\Exception as Exception;
use Icinga\Benchmark as Benchmark;
use Icinga\Protocol\Statusdat\View\MonitoringObjectList;
@ -36,7 +37,7 @@ use Icinga\Protocol\Statusdat\View\MonitoringObjectList;
* Class Reader
* @package Icinga\Protocol\Statusdat
*/
class Reader implements IReader
class Reader implements IReader, DatasourceInterface
{
/**
*

View File

@ -29,11 +29,11 @@
namespace Icinga\Protocol\Statusdat\View;
/**
* Class AbstractAccessorStrategy
* Basic interface for views.
* The name sound weirder than it is: Views define special get and exists operations for fields
* that are not directly available in a resultset, but exist under another name or can be
* accessed by loading an additional object during runtime.
* Interface for statusdat classes that provide a specific view on the dataset
*
* Views define special get and exists operations for fields that are not directly available
* in a resultset, but exist under another name or can be accessed by loading an additional object
* during runtime.
*
* @see Icinga\Backend\DataView\ObjectRemappingView For an implementation of mapping field names
* to storage specific names, e.g. service_state being status.current_state in status.dat views.
@ -41,9 +41,8 @@ namespace Icinga\Protocol\Statusdat\View;
* @see Icinga\Backend\MonitoringObjectList For the typical usage of this class. It is not wrapped
* around the monitoring object, so we don't use __get() or __set() and always have to give the
* item we'd like to access.
* @package Icinga\Backend\DataView
*/
interface AbstractAccessorStrategy
interface AccessorStrategy
{
/**
* Returns a field for the item, or throws an Exception if the field doesn't exist

View File

@ -2,7 +2,7 @@
/**
* Wrapper around an array of monitoring objects that can be enhanced with an optional
* object that extends AbstractAccessorStrategy. This will act as a dataview and provide
* object that extends AccessorStrategy. This will act as a dataview and provide
* normalized access to the underlying data (mapping properties, retrieving additional data)
*
* If not Accessor is set, this class just behaves like a normal Iterator and returns
@ -19,7 +19,7 @@ class MonitoringObjectList implements \Iterator, \Countable, \ArrayAccess
private $position = 0;
private $dataView = null;
function __construct(array &$dataset, AbstractAccessorStrategy $dataView = null)
function __construct(array &$dataset, AccessorStrategy $dataView = null)
{
$this->dataSet = $dataset;
$this->position = 0;

View File

@ -46,16 +46,16 @@ namespace Icinga\Protocol\Statusdat\View;
*/
class ObjectRemappingView implements AbstractAccessorStrategy
class ObjectRemappingView implements AccessorStrategy
{
/**
* When implementing your own Mapper, this contains the static mapping rules.
* @see Icinga\Backend\Statusdat\DataView\StatusdatServiceView for an example
* @see Monitoring\Backend\Statusdat\DataView\StatusdatServiceView for an example
*
* @var array
*/
protected $mappedParameters = array();
public static $mappedParameters = array();
private $functionMap = array(
"TO_DATE" => "toDateFormat"
@ -73,7 +73,7 @@ class ObjectRemappingView implements AbstractAccessorStrategy
/**
*
* @see Icinga\Backend\DataView\AbstractAccessorStrategy
* @see Icinga\Backend\DataView\AccessorStrategy
*
* @param The $item
* @param The $field
@ -86,7 +86,7 @@ class ObjectRemappingView implements AbstractAccessorStrategy
if (isset($item->$field)) {
return $item->$field;
}
if (isset($this->mappedParameters[$field])) {
if (isset(static::$mappedParameters[$field])) {
return $this->getMappedParameter($item, $field);
}
@ -118,7 +118,7 @@ class ObjectRemappingView implements AbstractAccessorStrategy
private function getMappedParameter(&$item, $field)
{
$matches = array();
$fieldDef = $this->mappedParameters[$field];
$fieldDef = static::$mappedParameters[$field];
$function = false;
if (preg_match_all('/(?P<FUNCTION>\w+)\((?P<PARAMETER>.*)\)/', $fieldDef, $matches)) {
$function = $matches["FUNCTION"][0];
@ -141,22 +141,22 @@ class ObjectRemappingView implements AbstractAccessorStrategy
/**
*
* @see Icinga\Backend\DataView\AbstractAccessorStrategy
* @see Icinga\Backend\DataView\AccessorStrategy
*
* @param The $field
* @return The|string
*/
public function getNormalizedFieldName($field)
{
if (isset($this->mappedParameters[$field])) {
return $this->mappedParameters[$field];
if (isset(static::$mappedParameters[$field])) {
return static::$mappedParameters[$field];
}
return $field;
}
/**
*
* @see Icinga\Backend\DataView\AbstractAccessorStrategy
* @see Icinga\Backend\DataView\AccessorStrategy
*
* @param The $item
* @param The $field
@ -165,7 +165,7 @@ class ObjectRemappingView implements AbstractAccessorStrategy
public function exists(&$item, $field)
{
return (isset($item->$field)
|| isset($this->mappedParameters[$field])
|| isset(static::$mappedParameters[$field])
|| isset($this->handlerParameters[$field])
);
}

View File

@ -83,22 +83,36 @@ class Monitoring_ListController extends ModuleActionController
$this->view->services = $this->query('status', array(
'host_name',
'host_problems',
'host_state',
'host_state_type',
'host_last_state_change',
'host_address',
'host_handled',
'service_description',
'service_display_name',
'service_state' => $state_column,
'service_in_downtime',
'service_acknowledged',
'service_handled',
'service_output',
'service_last_state_change' => $state_change_column
'service_last_state_change' => $state_change_column,
'service_icon_image',
'service_long_output',
'service_is_flapping',
'service_state_type',
'service_handled',
'service_severity',
'service_last_check',
'service_notifications_enabled',
'service_action_url',
'service_notes_url',
'service_last_comment'
));
$this->preserve('sort')
->preserve('backend')
->preserve('extracolumns');
$this->view->sort = $this->_getParam('sort');
if ($this->view->compact) {
$this->_helper->viewRenderer('services-compact');
if ($this->_getParam('sort')) {
$this->view->sort = $this->_getParam('sort');
}
}
public function hostgroupsAction()

View File

@ -27,6 +27,14 @@ class Zend_View_Helper_MonitoringState extends Zend_View_Helper_Abstract
if ($object->host_last_state_change > (time() - 600)) {
$state_classes[] = 'new';
}
} else {
$state_classes[] = $this->monitoringState($object, "service");
if ($object->service_acknowledged || $object->service_in_downtime) {
$state_classes[] = 'handled';
}
if ($object->service_last_state_change > (time() - 600)) {
$state_classes[] = 'new';
}
}
return $state_classes;

View File

@ -0,0 +1,171 @@
<?= $this->tabs ?>
<?php
$paginator = $services->paginate();
function getRowProperties(&$service, &$last_host, $scope) {
if ($last_host !== $service->host_name) {
$host_col = '<b>' . $scope->qlink(
$service->host_name,
'monitoring/show/host',
array('host' => $service->host_name)
) . ':</b><span style="font-size: 0.7em">'
. (isset($service->host->address) ? ' ( ' . $scope->escape($service->host->address) . ')' : '')
. '</span>';
$last_host = $service->host_name;
} else {
$host_col = '&nbsp; ';
}
$icons = array();
if ($service->service_acknowledged) {
$icons['ack.gif'] = 'Problem has been acknowledged';
}
if ($service->service_in_downtime) {
$icons['downtime.gif'] = 'Service is in a scheduled downtime';
}
if ($service->host_problems) {
$icons['server.png'] = 'This services host has a problem';
}
$state_classes = array($scope->monitoringState($service));
if ($service->service_handled) {
$state_classes[] = 'handled';
}
if ($service->service_last_state_change > (time() - 600)) {
$state_classes[] = 'new';
}
$state_title = strtoupper($scope->monitoringState($service))
. ' since '
. date('Y-m-d H:i:s', $service->service_last_state_change);
if ($scope->grapher && $scope->grapher->hasGraph($service->host_name, $service->service_description)) {
$graph = $scope->grapher->getSmallPreviewImage(
$service->host_name,
$service->service_description
);
} else {
$graph = '';
}
return array($host_col,$icons,$state_classes,$state_title,$graph);
}
$fparams = $this->services->getAppliedFilter()->toParams();
if ($this->preserve === null) {
$this->preserve = $fparams;
} else {
$this->preserve += $fparams;
}
$last_host = null;
$always = array();
if (isset($_GET['sort'])) {
$always['sort'] = $_GET['sort'];
}
if (isset($_GET['dir'])) {
$always['dir'] = $_GET['dir'];
}
?>
<div class="dontprint">
<? if (! empty($fparams)): ?>
<div style="float: right; width: 20em; font-size: 0.8em;"><b>Filters</b><br>
<? foreach ($fparams as $k => $v): ?>
<?= $this->qlink(
'x',
'monitoring/list/services',
$this->services->getAppliedFilter()->without($k)->toParams() + $always,
array(
'style' => array('color' => 'red')
)
) ?> <?= $this->escape("$k = $v") ?></br>
<? endforeach ?>
</div>
<? endif ?>
<form method="get" action="<?= $this->qUrl(
'monitoring/list/services?' . http_build_query(
$this->services->getAppliedFilter()->toParams()
),
array()
)
?>">
Sort by <?= $this->formSelect(
'sort',
$this->sort,
array(
'class' => 'autosubmit',
),
array(
'severity' => 'Severity',
'service_last_state_change' => 'Last state change',
'service_last_time_unknown' => 'Last UNKNOWN',
'service_last_time_critical' => 'Last CRITICAL',
'service_last_time_warning' => 'Last WARNING',
'service_last_time_ok' => 'Last OK',
'host_name' => 'Host',
'service_description' => 'Service',
)
) ?>
<?= $this->formText(
'search',
$this->search,
array(
'placeholder' => 'Add filllter...',
)
) ?>
</form>
</div>
<?php if (empty($services)): ?>
<div class="alert alert-info fullpage_infobox">
Sorry, no services found for this search
</div>
<?php return; endif ?>
<?= $this->paginationControl($paginator, null, null, array('preserve' => $this->preserve )); ?>
<table class="action">
<tbody>
<?php foreach ($services->fetchAll() as $service):
list($host_col,$icons,$state_classes,$state_title,$graph) = getRowProperties($service,$last_host,$this); ?>
<tr class="<?= implode(' ', $state_classes) ?>">
<td class="state" title="<?= $state_title ?>">
<?= $this->qlink(
$service->service_state == 99 ? 'PENDING' :
substr(strtoupper($this->monitoringState($service)), 0, 4)
. '&nbsp;since<br /> '
. $this->timeSince($service->service_last_state_change),
'monitoring/show/history', array(
'host' => $service->host_name,
'service' => $service->service_description
), array('quote' => false)) ?>
</td>
<td>
<?php
foreach ($icons as $icon => $alt) {
echo $this->img('img/classic/' . $icon, array(
'class' => 'icon',
'title' => $alt
));
} ?>
<?= $host_col ?>
<?= $this->qlink($service->service_description, 'monitoring/show/service', array(
'host' => $service->host_name,
'service' => $service->service_description
), array('class' => 'row-action')
)
?>
<br />
&nbsp; &nbsp; <span style="font-size: 0.7em">
<?= $this->escape(substr(strip_tags($service->service_output), 0, 900)) ?>
</span>
<?= $graph ?>
</td>
<? foreach ($this->extraColumns as $col): ?>
<td><?= $this->escape($service->$col) ?></td>
<? endforeach ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>

View File

@ -1,171 +1,180 @@
<?= $this->tabs ?>
<?php
<?php
$paginator = $services->paginate();
function getRowProperties(&$service, &$last_host, $scope) {
if ($last_host !== $service->host_name) {
$host_col = '<b>' . $scope->qlink(
$service->host_name,
'monitoring/show/host',
array('host' => $service->host_name)
) . ':</b><span style="font-size: 0.7em">'
. (isset($service->host->address) ? ' ( ' . $scope->escape($service->host->address) . ')' : '')
. '</span>';
$last_host = $service->host_name;
} else {
$host_col = '&nbsp; ';
}
$icons = array();
if ($service->service_acknowledged) {
$icons['ack.gif'] = 'Problem has been acknowledged';
}
if ($service->service_in_downtime) {
$icons['downtime.gif'] = 'Service is in a scheduled downtime';
}
if ($service->host_problems) {
$icons['server.png'] = 'This services host has a problem';
}
$state_classes = array($scope->monitoringState($service));
if ($service->service_handled) {
$state_classes[] = 'handled';
}
if ($service->service_last_state_change > (time() - 600)) {
$state_classes[] = 'new';
}
$state_title = strtoupper($scope->monitoringState($service))
. ' since '
. date('Y-m-d H:i:s', $service->service_last_state_change);
if ($scope->grapher && $scope->grapher->hasGraph($service->host_name, $service->service_description)) {
$graph = $scope->grapher->getSmallPreviewImage(
$service->host_name,
$service->service_description
);
} else {
$graph = '';
}
return array($host_col,$icons,$state_classes,$state_title,$graph);
}
$fparams = $this->services->getAppliedFilter()->toParams();
if ($this->preserve === null) {
$this->preserve = $fparams;
} else {
$this->preserve += $fparams;
}
$last_host = null;
$always = array();
if (isset($_GET['sort'])) {
$always['sort'] = $_GET['sort'];
}
if (isset($_GET['dir'])) {
$always['dir'] = $_GET['dir'];
}
$viewHelper = $this->getHelper('MonitoringState');
$trimArea = $this->getHelper('Trim');
?>
<div class="dontprint">
<? if (! empty($fparams)): ?>
<div style="float: right; width: 20em; font-size: 0.8em;"><b>Filters</b><br>
<? foreach ($fparams as $k => $v): ?>
<?= $this->qlink(
'x',
'monitoring/list/services',
$this->services->getAppliedFilter()->without($k)->toParams() + $always,
array(
'style' => array('color' => 'red')
)
) ?> <?= $this->escape("$k = $v") ?></br>
<? endforeach ?>
</div>
<? endif ?>
<form method="get" action="<?= $this->qUrl(
'monitoring/list/services?' . http_build_query(
$this->services->getAppliedFilter()->toParams()
),
array()
)
?>">
Sort by <?= $this->formSelect(
'sort',
$this->sort,
array(
'class' => 'autosubmit',
),
array(
'severity' => 'Severity',
'service_last_state_change' => 'Last state change',
'service_last_time_unknown' => 'Last UNKNOWN',
'service_last_time_critical' => 'Last CRITICAL',
'service_last_time_warning' => 'Last WARNING',
'service_last_time_ok' => 'Last OK',
'host_name' => 'Host',
'service_description' => 'Service',
)
) ?>
<?= $this->formText(
'search',
$this->search,
array(
'placeholder' => 'Add filllter...',
)
) ?>
</form>
</div>
<?php if (empty($services)): ?>
<div class="alert alert-info fullpage_infobox">
Sorry, no services found for this search
</div>
<div class="alert alert-info fullpage_infobox">
Sorry, no services found for this search
</div>
<?php return; endif ?>
<?= $this->paginationControl($paginator, null, null, array('preserve' => $this->preserve )); ?>
<table class="action">
<tbody>
<?php foreach ($services->fetchAll() as $service):
list($host_col,$icons,$state_classes,$state_title,$graph) = getRowProperties($service,$last_host,$this); ?>
<tr class="<?= implode(' ', $state_classes) ?>">
<td class="state" title="<?= $state_title ?>">
<?= $this->qlink(
$service->service_state == 99 ? 'PENDING' :
substr(strtoupper($this->monitoringState($service)), 0, 4)
. '&nbsp;since<br /> '
. $this->timeSince($service->service_last_state_change),
'monitoring/show/history', array(
'host' => $service->host_name,
'service' => $service->service_description
), array('quote' => false)) ?>
</td>
<td>
<?php
foreach ($icons as $icon => $alt) {
echo $this->img('img/classic/' . $icon, array(
'class' => 'icon',
'title' => $alt
));
} ?>
<?= $host_col ?>
<?= $this->qlink($service->service_description, 'monitoring/show/service', array(
'host' => $service->host_name,
'service' => $service->service_description
), array('class' => 'row-action')
)
?>
<br />
&nbsp; &nbsp; <span style="font-size: 0.7em">
<?= $this->escape(substr(strip_tags($service->service_output), 0, 900)) ?>
</span>
<?= $graph ?>
</td>
<? foreach ($this->extraColumns as $col): ?>
<td><?= $this->escape($service->$col) ?></td>
<? endforeach ?>
</tr>
<?php endforeach; ?>
<form method="get" action="<?= $this->qUrl(
'monitoring/list/services?' . http_build_query($this->services->getAppliedFilter()->toParams()),
array()
);
?>">
Sort by <?= $this->formSelect(
'sort',
$this->sort,
array('class' => 'autosubmit'),
array(
'service_severity' => 'Severity',
'service_last_state_change' => 'Last state change',
'service_last_time_unknown' => 'Last UNKNOWN',
'service_last_time_critical' => 'Last CRITICAL',
'service_last_time_warning' => 'Last WARNING',
'service_last_time_ok' => 'Last OK',
'service_description' => 'Service',
)
) ?>
<input type="search" name="filter" placeholder="Type to filter" />
<button class="btn btn-small"><i class="icon-refresh"></i></button>
</form>
<?= $this->paginationControl($paginator, null, null, array('preserve' => $this->preserve)) ?>
<table class="statustable action services">
<thead>
<tr>
<th colspan="3">Status</th>
<th>Service</th>
<th>Host</th>
<th>Output</th>
<th></th>
</tr>
</thead>
<tbody>
<?php foreach ($services->fetchAll() as $service): ?>
<tr class="<?= implode(' ', $viewHelper->getStateFlags($service, 'service')); ?>">
<td class="icons indicator">
<div class="img-box"><?php $trimArea->start(); ?>
<?php if ($service->service_icon_image) : ?>
<img src="<?= $service->service_icon_image; ?>"/>
<?php endif; ?>
<?php $trimArea->end(); ?></div>
</td>
<td class="icons indicator">
<div class="icon-box"><?php $trimArea->start(); ?>
<?php if (!$service->service_handled && $service->service_state > 0): ?>
<a href="#" title="<?= 'Unhandled service' ?>">
<i class="icon-warning-sign"></i>
</a>
<?php endif; ?>
<?php if ($service->service_acknowledged && !$service->service_in_downtime): ?>
<a href="#" title="<?= 'Acknowledged' ?>">
<i class="icon-ok-sign"></i>
</a>
<?php endif; ?>
<?php if ($service->service_is_flapping): ?>
<a href="#" title="<?= 'Flapping' ?>">
<i class="icon-random"></i>
</a>
<?php endif; ?>
<?php if (!$service->service_notifications_enabled): ?>
<a href="#" title="<?= 'Notifications disabled' ?>">
<i class="icon-volume-off"></i>
</a>
<?php endif; ?>
<?php if ($service->service_in_downtime): ?>
<a href="#" title="<?= 'In downtime' ?>">
<i class="icon-wrench"></i>
</a>
<?php endif; ?>
<?php $trimArea->end(); ?></div>
</td>
<td class="indicator state" title="<?= $viewHelper->getStateTitle($service, 'service'); ?>">
<div class="statetext">
<?= $this->qlink(
"<b>".ucfirst($viewHelper->monitoringState($service, 'service'))."</b>".
'<div class="nowrap"> since&nbsp;'.
$this->timeSince($service->service_last_state_change),
'monitoring/show/history', array(
'host' => $service->host_name,
'service' => $service->service_description
),
array('quote' => false)
);?>
<?php if ($service->service_state_type == 0): ?>
<a href="#" title="<?= 'Soft state' ?>">
<i class="icon-gears"></i>
</a>
<?php endif; ?>
</div>
</td>
<td class="servicename">
<?php if ($service->service_last_comment !== null): ?>
<a href="#" title="<?= 'Comments' ?>">
<i class="icon-comment"> </i>
</a>
<?php endif; ?>
<?= $this->qlink(
"<b>".$service->service_display_name."</b><br/>",
'monitoring/show/service', array(
'host' => $service->host_name,
'service' => $service->service_description
), array(
'class' => 'row-action',
'quote' => false
)
); ?>
<?php if ($service->service_action_url != ""): ?>
<a href="<?= $service->service_action_url; ?>">Action</a>
<?php endif; ?>
<?php if ($service->service_notes_url != ""): ?>
<a href="<?= $service->service_notes_url; ?>">Notes</a>
<?php endif; ?>
<?php if ($service->service_state_type == 0): ?>
<a href="#" title="<?= 'Soft state' ?>">
<i class="icon-gears"></i>
</a>
<?php endif; ?>
</td>
<td class="hostname" title="<?= $viewHelper->getStateTitle($service, 'host'); ?>">
<?= $this->qlink(
$service->host_name,
'monitoring/show/host', array(
'host' => $service->host_name
), array(
'class' => 'row-action',
'quote' => false
)
); ?>
<div class="statetext">
<?= $this->qlink(
"(".ucfirst($viewHelper->monitoringState($service, 'host')).")",
'monitoring/show/history', array(
'host' => $service->host_name,
'service' => $service->service_description
),
array('quote' => false)
);?>
</div>
<span class="host_address">
<?= $service->host_address ?>
</span>
</td>
<td class="expand-full">
<?= $this->escape(substr(strip_tags($service->service_output), 0, 10000)); ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>

View File

@ -54,6 +54,7 @@ class AbstractBackend implements DatasourceInterface
)
);
}
$query = new $classname($this, $fields);
return $query;
}

View File

@ -153,11 +153,10 @@ abstract class AbstractQuery extends Query
} elseif ($this->hasAliasName($col)) {
$col = $this->aliasToColumnName($col);
} else {
die('SHIT');
throw new \InvalidArgumentException('Can\'t order by column '.$col);
}
$this->order_columns[] = array($col, $dir);
return $this;
return parent::order($col, $dir);
}
public function setRealColumns()

View File

@ -113,6 +113,8 @@ class StatusQuery extends AbstractQuery
'service_description' => 'so.name2 COLLATE latin1_general_ci',
'service_display_name' => 's.display_name',
'service_icon_image' => 's.icon_image',
'service_action_url' => 's.action_url',
'service_notes_url' => 's.notes_url'
),
'servicestatus' => array(
@ -123,9 +125,10 @@ class StatusQuery extends AbstractQuery
'service_output' => 'ss.output',
'service_long_output' => 'ss.long_output',
'service_perfdata' => 'ss.perfdata',
'service_is_flapping' => 'ss.is_flapping',
'service_acknowledged' => 'ss.problem_has_been_acknowledged',
'service_in_downtime' => 'CASE WHEN (ss.scheduled_downtime_depth = 0) THEN 0 ELSE 1 END',
'service_handled' => 'CASE WHEN (ss.problem_has_been_acknowledged + ss.scheduled_downtime_depth + COALESCE(hs.current_state, 0)) > 0 THEN 1 ELSE 0 END',
'service_handled' => 'CASE WHEN (COALESCE(ss.current_state, 0) * ss.problem_has_been_acknowledged + ss.scheduled_downtime_depth + COALESCE(hs.current_state, 0)) > 0 THEN 1 ELSE 0 END',
'service_does_active_checks' => 'ss.active_checks_enabled',
'service_accepts_passive_checks' => 'ss.passive_checks_enabled',
'service_last_state_change' => 'UNIX_TIMESTAMP(ss.last_state_change)',
@ -152,9 +155,9 @@ class StatusQuery extends AbstractQuery
'service_last_comment' => 'slc.comment_id'
),
'status' => array(
'problems' => 'CASE WHEN ss.current_state = 0 THEN 0 ELSE 1 END',
'handled' => 'CASE WHEN ss.problem_has_been_acknowledged = 1 OR ss.scheduled_downtime_depth > 0 THEN 1 ELSE 0 END',
'severity' => 'CASE WHEN ss.current_state = 0
'service_problems' => 'CASE WHEN ss.current_state = 0 THEN 0 ELSE 1 END',
'service_handled' => 'CASE WHEN ss.problem_has_been_acknowledged = 1 OR ss.scheduled_downtime_depth > 0 THEN 1 ELSE 0 END',
'service_severity' => 'CASE WHEN ss.current_state = 0
THEN
CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL
THEN 16
@ -345,4 +348,16 @@ class StatusQuery extends AbstractQuery
array()
);
}
protected function joinLastservicecomment()
{
$this->baseQuery->joinleft(
array ('slc' => new \Zend_Db_Expr(
'(SELECT MAX(c.comment_id) as comment_id, c.object_id '.
'FROM icinga_comments c GROUP BY c.object_id)')
),
'slc.object_id = ss.service_object_id',
array()
);
}
}

View File

@ -35,7 +35,7 @@ use Icinga\Protocol\Statusdat\IReader;
* Class StatusdatHostView
* @package Icinga\Backend\Statusdat\DataView
*/
class StatusdatHostView extends ObjectRemappingView
class HostStatusView extends ObjectRemappingView
{
/**
* @var mixed
@ -49,7 +49,8 @@ class StatusdatHostView extends ObjectRemappingView
"host" => "getHost",
"host_unhandled_service_count" => "getNrOfUnhandledServices",
"host_last_comment" => "getLastComment",
'host_handled' => "checkIfHandled"
'host_handled' => "checkIfHandled",
);
public function checkIfHandled(&$host)
@ -83,20 +84,21 @@ class StatusdatHostView extends ObjectRemappingView
/**
* @var array
*/
protected $mappedParameters = array(
public static $mappedParameters = array(
"host_address" => "address",
"host_name" => "host_name",
"host" => "host_name",
"host_state" => "status.current_state",
"host_output" => "status.plugin_output",
"host_long_output" => "status.long_plugin_output",
"host_perfdata" => "status.pluign",
"host_perfdata" => "status.performance_data",
"host_last_state_change" => "status.last_state_change",
"host_check_command" => "check_command",
"host_last_check" => "TO_DATE(status.last_check)",
"host_next_check" => "status.next_check",
"host_check_latency" => "status.check_latency",
"host_check_execution_time" => "status.check_execution_time",
"active_checks_enabled" => "status.active_checks_enabled",
"host_active_checks_enabled" => "status.active_checks_enabled",
"host_in_downtime" => "status.scheduled_downtime_depth",
"host_is_flapping" => "status.is_flapping",
"host_notifications_enabled"=> "status.notifications_enabled",
@ -104,7 +106,7 @@ class StatusdatHostView extends ObjectRemappingView
"host_icon_image" => "icon_image",
"host_action_url" => "action_url",
"host_notes_url" => "notes_url",
"host_acknowledged" => "status.problem_has_been_acknowledged",
"host_acknowledged" => "status.problem_has_been_acknowledged"
// "state" => "current_state"
);
@ -114,7 +116,7 @@ class StatusdatHostView extends ObjectRemappingView
*/
public function getHost(&$item)
{
if (!isset($this->state["host"][$item->host_name])) {
if (!isset($this->state["service"][$item->host_name])) {
return null;
}
if (!isset($this->state["host"][$item->host_name])) {

View File

@ -0,0 +1,163 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Monitoring\Backend\Statusdat\DataView;
use Icinga\Protocol\Statusdat\View\ObjectRemappingView;
use Icinga\Protocol\Statusdat\IReader;
/**
* Class StatusdatHostView
* @package Icinga\Backend\Statusdat\DataView
*/
class ServiceStatusView extends ObjectRemappingView
{
/**
* @var mixed
*/
private $state;
/**
* @var array
*/
protected $handlerParameters = array(
"host" => "getHost",
"host_last_comment" => "getLastHostComment",
'host_handled' => "checkIfHostHandled",
'service_handled' => "checkIfHandled",
"service_last_comment" => "getLastComment"
);
public function checkIfHostHandled(&$service)
{
return $service->host->status->current_state == 0 ||
$service->host->status->problem_has_been_acknowledged ||
$service->host->status->scheduled_downtime_depth;
}
public function checkIfHandled(&$service)
{
return $service->status->current_state == 0 ||
$service->status->problem_has_been_acknowledged ||
$service->status->scheduled_downtime_depth;
}
public function getLastComment(&$service)
{
if (!isset($service->comment) || empty($service->comment)) {
return null;
}
$comment = end($service->comment);
return $comment->comment_id;
}
public function getLastHostComment(&$service)
{
if (!isset($service->host->comment) || empty($service->host->comment)) {
return null;
}
$comment = end($service->host->comment);
return $comment->comment_id;
}
/**
* @var array
*/
public static $mappedParameters = array(
"host_address" => "host.address",
"host_name" => "host.host_name",
"host" => "host.host_name",
"host_state" => "host.status.current_state",
"host_output" => "host.status.plugin_output",
"host_long_output" => "host.status.long_plugin_output",
"host_perfdata" => "host.status.performance_data",
"host_last_state_change" => "host.status.last_state_change",
"host_check_command" => "host.check_command",
"host_last_check" => "TO_DATE(host.status.last_check)",
"host_next_check" => "host.status.next_check",
"host_check_latency" => "host.status.check_latency",
"host_check_execution_time" => "host.status.check_execution_time",
"host_active_checks_enabled" => "host.status.active_checks_enabled",
"host_in_downtime" => "host.status.scheduled_downtime_depth",
"host_is_flapping" => "host.status.is_flapping",
"host_notifications_enabled" => "host.status.notifications_enabled",
"host_state_type" => "host.status.state_type",
"host_icon_image" => "host.icon_image",
"host_action_url" => "host.action_url",
"host_notes_url" => "host.notes_url",
"host_acknowledged" => "host.status.problem_has_been_acknowledged",
"service" => "service_description",
"service_display_name" => "service_description",
"service_description" => "service_description",
"service_state" => "status.current_state",
"service_icon_image" => "icon_image",
"service_output" => "status.plugin_output",
"service_long_output" => "status.long_plugin_output",
"service_perfdata" => "status.performance_data",
"service_last_state_change" => "status.last_state_change",
"service_check_command" => "check_command",
"service_last_check" => "TO_DATE(status.last_check)",
"service_next_check" => "status.next_check",
"service_check_latency" => "status.check_latency",
"service_check_execution_time" => "status.check_execution_time",
"service_active_checks_enabled" => "status.active_checks_enabled",
"service_in_downtime" => "status.scheduled_downtime_depth",
"service_is_flapping" => "status.is_flapping",
"service_notifications_enabled" => "status.notifications_enabled",
"service_state_type" => "status.state_type",
"service_icon_image" => "icon_image",
"service_action_url" => "action_url",
"service_notes_url" => "notes_url",
"service_acknowledged" => "status.problem_has_been_acknowledged",
// "state" => "current_state"
);
/**
* @param $item
* @return null
*/
public function getHost(&$item)
{
if (!isset($this->state["service"][$item->host_name])) {
return null;
}
if (!isset($this->state["host"][$item->host_name])) {
return null;
}
return $this->state["host"][$item->host_name];
}
/**
* @param IReader $reader
*/
public function __construct(IReader $reader)
{
$this->state = & $reader->getState();
}
}

View File

@ -49,7 +49,7 @@ class StatusdatServiceView extends ObjectRemappingView
/**
* @var array
*/
protected $mappedParameters = array(
public static $mappedParameters = array(
"host_address" => "parenthost.address",
"host_name" => "host_name",
"active_checks_enabled" => "status.active_checks_enabled",

View File

@ -33,6 +33,7 @@ use Icinga\Protocol\Statusdat;
use Icinga\Exception;
use Icinga\Data\AbstractQuery;
use Icinga\Protocol\Statusdat\View\MonitoringObjectList as MList;
use Icinga\Protocol\Statusdat\Query as StatusdatQuery;
/**
* Class Query
* @package Icinga\Backend\Statusdat
@ -42,24 +43,24 @@ abstract class Query extends AbstractQuery
/**
* @var null
*/
protected $cursor = null;
private $cursor = null;
/**
* @var string
*/
protected $view = 'Monitoring\Statusdat\DataView\StatusdatServiceView';
private $viewClass = '\Monitoring\Backend\Statusdat\DataView\StatusdatServiceView';
private $baseQuery = null;
public function setBaseQuery(StatusdatQuery $query)
{
$this->baseQuery = $query;
}
public function setResultViewClass($viewClass)
{
$this->viewClass = '\Monitoring\Backend\Statusdat\DataView\\'.$viewClass;
}
/**
* @var array Mapping of order to field names
* @todo Is not complete right now
*/
protected $orderColumns = array(
Order::SERVICE_STATE => "status.current_state",
Order::STATE_CHANGE => "status.last_state_change",
Order::HOST_STATE => "status.current_state",
Order::HOST_NAME => "host_name",
Order::SERVICE_NAME => "service_description"
);
/**
* Calls the apply%Filtername%Filter() method for the given filter, or simply calls
@ -113,7 +114,7 @@ abstract class Query extends AbstractQuery
$text = "%$value%";
$val = array($text, $text, $text);
$this->query->where("(host_name LIKE ? OR service_description LIKE ? OR status.plugin_output LIKE ?)", $val);
$this->baseQuery->where("(host_name LIKE ? OR service_description LIKE ? OR status.plugin_output LIKE ?)", $val);
}
@ -126,7 +127,7 @@ abstract class Query extends AbstractQuery
public function applyHostgroupsFilter($type, $value)
{
$filter = array($value);
$this->query->where("host.group IN ?", $filter);
$this->baseQuery->where("host.group IN ?", $filter);
}
/**
@ -138,7 +139,7 @@ abstract class Query extends AbstractQuery
public function applyServicegroupsFilter($type, $value)
{
$filter = array($value);
$this->query->where("group IN ?", $filter);
$this->baseQuery->where("group IN ?", $filter);
}
/**
@ -151,7 +152,7 @@ abstract class Query extends AbstractQuery
public function applyHandledFilter($type, $value)
{
$val = array($value, $value);
$this->query->where("(status.problem_has_been_acknowledged = ? )", $val);
$this->baseQuery->where("(status.problem_has_been_acknowledged = ? )", $val);
}
/**
@ -163,7 +164,7 @@ abstract class Query extends AbstractQuery
if (!is_array($value)) {
$value = array($value);
}
$this->query->where("host_name LIKE ?", $value);
$this->baseQuery->where("host_name LIKE ?", $value);
}
/**
@ -172,7 +173,7 @@ abstract class Query extends AbstractQuery
*/
public function applyStateFilter($type, $value)
{
$this->query->where("status.current_state = $value");
$this->baseQuery->where("status.current_state = $value");
}
/**
@ -181,7 +182,7 @@ abstract class Query extends AbstractQuery
*/
public function applyHoststateFilter($type, $value)
{
$this->query->where("host.status.current_state = $value");
$this->baseQuery->where("host.status.current_state = $value");
}
/**
@ -193,7 +194,7 @@ abstract class Query extends AbstractQuery
if (!is_array($value)) {
$value = array($value);
}
$this->query->where("service_description LIKE ?", $value);
$this->baseQuery->where("service_description LIKE ?", $value);
}
/**
@ -204,7 +205,7 @@ abstract class Query extends AbstractQuery
*/
public function limit($count = null, $offset = null)
{
$this->query->limit($count, $offset);
$this->baseQuery->limit($count, $offset);
return $this;
}
@ -219,7 +220,8 @@ abstract class Query extends AbstractQuery
{
if ($column) {
$this->query->order($this->orderColumns[$column], strtolower($dir));
$class = $this->viewClass;
$this->baseQuery->order($class::$mappedParameters[$column], strtolower($dir));
}
return $this;
}
@ -237,7 +239,7 @@ abstract class Query extends AbstractQuery
if (!is_array($value)) {
$value = array($value);
}
$this->query->where($column, $value);
$this->baseQuery->where($column, $value);
return $this;
}
@ -246,9 +248,9 @@ abstract class Query extends AbstractQuery
*/
public function fetchAll()
{
$view = $this->view;
$view = $this->viewClass;
if (!$this->cursor) {
$this->cursor = new MList($this->query->getResult(), new $view($this->reader));
$this->cursor = new MList($this->baseQuery->getResult(), new $view($this->reader));
}
return $this->cursor;
}
@ -283,6 +285,6 @@ abstract class Query extends AbstractQuery
public function count()
{
return count($this->query->getResult());
return count($this->baseQuery->getResult());
}
}

View File

@ -15,22 +15,24 @@ use Icinga\Exception;
class StatusQuery extends Query
{
/**
* @var \Icinga\Protocol\Statusdat\Query
*/
protected $query;
/**
* @var string
*/
protected $view = 'Monitoring\Backend\Statusdat\DataView\StatusdatHostView';
private function getTarget()
{
foreach($this->getColumns() as $column) {
if(preg_match("/^service/",$column))
return "service";
}
return "host";
}
public function init()
{
$target = $this->getTarget();
$this->reader = $this->ds->getReader();
$this->query = $this->reader->select()->from("hosts", array());
$this->setResultViewClass(ucfirst($target)."StatusView");
$this->setBaseQuery($this->reader->select()->from($target."s", array()));
}
}

View File

@ -135,6 +135,7 @@ class MonitoringView extends AbstractQuery
*/
protected function applyRequestSorting($request)
{
return $this->order(
// TODO: Use first sortDefaults entry if available, fall back to
// column if not
@ -238,12 +239,12 @@ class MonitoringView extends AbstractQuery
-4
) . 'Query';
$class = '\\' . get_class($this->ds) . '\\Query\\' . $class;
$query = new $class($this->ds, $this->columns);
foreach ($this->filters as $f) {
$query->where($f[0], $f[1]);
}
foreach ($this->order_columns as $col) {
if (isset($this->sortDefaults[$col[0]]['columns'])) {
foreach ($this->sortDefaults[$col[0]]['columns'] as $c) {
$query->order($c, $col[1]);
@ -252,11 +253,13 @@ class MonitoringView extends AbstractQuery
$query->order($col[0], $col[1]);
}
}
$this->query = $query;
}
if ($this->hasLimit()) {
$this->query->limit($this->getLimit(), $this->getOffset());
}
return $this->query;
}

View File

@ -96,7 +96,6 @@ class ListControllerHostMySQLTest extends MonitoringControllerTest
$this->assertEquals("note1.html", $hostToTest->host_notes_url, 'Testing for notes url (backend '.$backend.')');
$this->assertEquals("action.html", $hostToTest->host_action_url, 'Testing for action url (backend '.$backend.')');
$this->assertEquals(2, $hostToTest->host_unhandled_service_count, 'Testing correct open problems count (backend '.$backend.')');
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace Test\Monitoring\Application\Controllers\ListController;
require_once(dirname(__FILE__).'/../../testlib/MonitoringControllerTest.php');
use Test\Monitoring\Testlib\MonitoringControllerTest;
use Test\Monitoring\Testlib\Datasource\TestFixture;
use Test\Monitoring\Testlib\Datasource\ObjectFlags;
class ListControllerServiceMySQLTest extends MonitoringControllerTest
{
public function testServiceListMySQL()
{
$this->executeServiceListTestFor("mysql");
}
public function testServiceListPgSQL()
{
$this->executeServiceListTestFor("pgsql");
}
public function testServiceListStatusdat()
{
$this->executeServiceListTestFor("statusdat");
}
public function executeServiceListTestFor($backend)
{
date_default_timezone_set('UTC');
$checkTime = time()-2000;
$fixture = new TestFixture();
$fixture->addHost('host1', 0)->
addService("svc1", 0, new ObjectFlags(2000), array(
"notes_url" => "notes.url",
"action_url" => "action.url",
"icon_image" => "svcIcon.png"
))->
addService("svcDown", 2) -> addComment("author", "Comment text")->
addService("svcFlapping", 1, ObjectFlags::FLAPPING())->addToServicegroup("Warning")->
addService("svcNotifDisabled", 2, ObjectFlags::DISABLE_NOTIFICATIONS())->
addService("svcPending", 0, ObjectFlags::PENDING());
$fixture->addHost('host2', 1)->
addService("svcPassive", 1, ObjectFlags::PASSIVE_ONLY())->addToServicegroup("Warning")->
addService("svcDisabled", 1, ObjectFlags::DISABLED())->addToServicegroup("Warning")->
addService("svcDowntime", 2, ObjectFlags::IN_DOWNTIME())->
addService("svcAcknowledged", 1, ObjectFlags::ACKNOWLEDGED())->addToServicegroup("Warning");
try {
$this->setupFixture($fixture, $backend);
} catch (\PDOException $e) {
echo $e->getMessage();
$this->markTestSkipped('Could not setup fixture for backends '.$backend.' :'.$e->getMessage());
return null;
}
$controller = $this->requireController('ListController', $backend);
$controller->servicesAction();
$result = $controller->view->services->fetchAll();
$this->assertEquals(9, count($result), "Testing for correct service count");
$this->assertEquals("notes.url", $result[0]->service_notes_url, "Testing for correct notes_url");
$this->assertEquals("action.url", $result[0]->service_action_url, "Testing for correct action_url");
$this->assertEquals(0, $result[0]->service_state, "Testing for correct Service state");
}
}

View File

@ -16,6 +16,8 @@ namespace Icinga\Web
*/
public $view;
public $headers = array();
/**
* Parameters provided on call
* @var array
@ -28,14 +30,29 @@ namespace Icinga\Web
* @param string $param The parameter name to retrieve
* @return mixed|bool The parameter $param or false if it doesn't exist
*/
public function _getParam($param)
public function _getParam($param, $default = null)
{
if (!isset($this->params[$param])) {
return false;
return $default;
}
return $this->params[$param];
}
public function getParam($param, $default = null)
{
return $this->_getParam($param, $default);
}
public function preserve()
{
return $this;
}
public function getParams()
{
return $this->params;
}
/**
* Sets the backend for this controller which will be used in the action
*
@ -45,6 +62,17 @@ namespace Icinga\Web
{
$this->backend = $backend;
}
public function __get($param) {
return $this;
}
public function getHeader($header) {
if (isset($this->headers[$header])) {
return $this->headers[$header];
}
return null;
}
}
}

View File

@ -170,14 +170,14 @@ class PDOInsertionStrategy
$insertObjectQuery->execute(array($this->objectId, $service["host"]["name"], $service["name"]));
$insertServiceQuery->execute(array(
$this->objectId, $service['host']['object_id'], $this->objectId, $service['name'],
$service["notes_url"], $service["action_url"], $service["icon_image"]
$service["icon_image"], $service["notes_url"], $service["action_url"]
));
$insertServiceStatusQuery->execute(array(
$this->objectId, $service["state"], date($this->datetimeFormat, $flags->time),
date($this->datetimeFormat, $flags->time), $flags->notifications, $flags->active_checks,
$flags->passive_checks, $flags->flapping, $flags->in_downtime, "Plugin output for service ".$service["name"],
"Long plugin output for service ".$service["name"], $flags->acknowledged,
$flags->is_pending == 0
$flags->is_pending == 0 ? '1' : '0'
));
foreach($service["contacts"] as $contact) {