add support for icingadb as only icingaweb2 data backend

This commit is contained in:
Tobias Tiederle 2022-10-12 15:08:55 +00:00 committed by Sukhwinder Dhillon
parent 12cca3ebcf
commit 1c6090193d
11 changed files with 597 additions and 22 deletions

View File

@ -33,8 +33,8 @@ class HostController extends ObjectController
{
$host = $this->getHostObject();
$auth = $this->Auth();
$mon = $this->monitoring();
if ($this->isServiceAction() && $mon->canModifyService($host, $this->getParam('service'))) {
$backend = $this->backend();
if ($this->isServiceAction() && $backend->authCanEditService($auth, $host, $this->getParam('service'))) {
return;
}
if ($auth->hasPermission(Permission::MONITORING_SERVICES_RO) && $this->isServicesReadOnlyAction()) {
@ -43,7 +43,7 @@ class HostController extends ObjectController
if ($auth->hasPermission(Permission::HOSTS)) { // faster
return;
}
if ($mon->canModifyHost($host)) {
if ($backend->authCanEditHost($host)) {
return;
}
$this->assertPermission(Permission::HOSTS); // complain about default hosts permission
@ -564,13 +564,20 @@ class HostController extends ObjectController
{
$host = $this->object;
try {
if ($host->isObject() && $host instanceof IcingaHost && $this->monitoring()->hasHost($host)) {
$this->actions()->add(Link::create($this->translate('Show'), 'monitoring/host/show', [
'host' => $host->getObjectName()
], [
'class' => 'icon-globe critical',
'data-base-target' => '_next'
]));
$backend = $this->backend();
if ($host instanceof IcingaHost
&& $backend->isAvailable()
&& $host->isObject()
&& $backend->hasHost($host->getObjectName())
) {
$this->actions()->add($backend->getHostLink(
$this->translate('Show'),
$host->getObjectName(),
[
'class' => 'icon-globe critical',
'data-base-target' => '_next'
]
));
// Intentionally placed here, show it only for deployed Hosts
$this->addOptionalInspectLink();

View File

@ -30,10 +30,8 @@ class ServiceController extends ObjectController
protected function checkDirectorPermissions()
{
if ($this->hasPermission(Permission::MONITORING_SERVICES)) {
if ($this->host && $service = $this->object) {
if ($this->monitoring()->canModifyService($this->host, $service->getObjectName())) {
return;
}
if ($this->backend()->authCanEditService($this->Auth(), $this->getParam('host'), $this->getParam('name'))) {
return;
}
}
$this->assertPermission('director/hosts');

View File

@ -0,0 +1,151 @@
<?php
namespace Icinga\Module\Director;
use Icinga\Application\Icinga;
use Icinga\Authentication\Auth;
use Icinga\Data\Filter\Filter;
class Backend implements MonitorBackend
{
const MONITORING = 'monitoring';
const ICINGADB = 'icingadb';
protected $backend = null;
/**
* @param string|null $backend_name backend to use, 'icingadb' or 'monitoring'
* <code>null</code> will use either, preferring icingadb
*/
public function __construct($backend_name = null)
{
$app = Icinga::app();
$modules = $app->getModuleManager();
$tried_loading = false;
if (is_null($backend_name) || ($backend_name == self::ICINGADB)) {
if (!$modules->hasLoaded(self::ICINGADB) && $app->isCli()) {
$modules->loadEnabledModules();
$tried_loading = true;
}
if ($modules->hasLoaded(self::ICINGADB)) {
$this->backend = new MonitorBackendIcingadb();
}
}
if (is_null($this->backend)
&& (is_null($backend_name) || ($backend_name == self::MONITORING))) {
if (!$tried_loading && !$modules->hasLoaded(self::MONITORING) && $app->isCli()) {
$modules->loadEnabledModules();
}
if ($modules->hasLoaded(self::MONITORING)) {
$this->backend = new MonitorBackendMonitoring();
}
}
}
public function isAvailable()
{
return (($this->backend !== null) && ($this->backend->isAvailable()));
}
public function hasHost($hostname)
{
return (($this->backend === null) || $this->backend->hasHost($hostname));
}
public function hasService($hostname, $service)
{
return (($this->backend === null) || $this->backend->hasService($hostname, $service));
}
public function authCanEditHost(Auth $auth, $hostname, $service)
{
if ($auth->hasPermission('director/monitoring/hosts')) {
$restriction = null;
foreach ($auth->getRestrictions('director/monitoring/rw-object-filter') as $restriction) {
if ($this->hasHostWithExtraFilter($hostname, Filter::fromQueryString($restriction))) {
return true;
}
}
if ($restriction === null) {
return $this->hasHost($hostname);
}
}
return false;
}
public function authCanEditService(Auth $auth, $hostname, $service)
{
if ($hostname === null || $service === null) {
// TODO: UUID support!
return false;
}
if ($auth->hasPermission('director/monitoring/services')) {
$restriction = null;
foreach ($auth->getRestrictions('director/monitoring/rw-object-filter') as $restriction) {
if ($this->hasServiceWithExtraFilter($hostname, $service, Filter::fromQueryString($restriction))) {
return true;
}
}
if ($restriction === null) {
return $this->hasService($hostname, $service);
}
}
return false;
}
public function hasHostWithExtraFilter($hostname, Filter $filter)
{
if ($this->backend === null) {
return false;
}
return $this->backend->select()->from('hostStatus', [
'hostname' => 'host_name',
])->where('host_name', $hostname)->applyFilter($filter)->fetchOne() === $hostname;
}
public function hasServiceWithExtraFilter($hostname, $service, Filter $filter)
{
if ($this->backend === null) {
return false;
}
return (array) $this
->prepareServiceKeyColumnQuery($hostname, $service)
->applyFilter($filter)
->fetchRow() === [
'hostname' => $hostname,
'service' => $service,
];
}
public function getHostLink($title, $hostname, array $attributes = null)
{
if ($this->backend !== null) {
return $this->backend->getHostLink($title, $hostname, $attributes);
}
return null;
}
public function getHostState($hostname)
{
if ($this->backend === null) {
return (object) [
'hostname' => $hostname,
'state' => 'pending',
'problem' => '0',
'acknowledged' => '0',
'in_downtime' => '0',
'output' => null,
];
} else {
return $this->backend->getHostState($hostname);
}
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Icinga\Module\Director;
interface MonitorBackend
{
public function isAvailable();
public function hasHost($hostname);
public function hasService($hostname, $service);
public function getHostLink($title, $hostname, array $attributes = null);
public function getHostState($hostname);
}

View File

@ -0,0 +1,111 @@
<?php
namespace Icinga\Module\Director;
use gipfl\IcingaWeb2\Link;
use Icinga\Application\Icinga;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Common\Database;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Module\Icingadb\Model\Service;
use Icinga\Module\Icingadb\Redis\VolatileStateResults;
use ipl\Stdlib\Filter;
class MonitorBackendIcingadb implements MonitorBackend
{
use Database;
use Auth;
public function isAvailable()
{
$app = Icinga::app();
$modules = $app->getModuleManager();
return $modules->hasLoaded('icingadb');
}
public function hasHost($hostname)
{
$query = Host::on($this->getDb());
$query->filter(Filter::equal('host.name', $hostname));
$this->applyRestrictions($query);
/** @var Host $host */
$host = $query->first();
return ($host !== null);
}
public function hasService($hostname, $service)
{
$query = Service::on($this->getDb());
$query
->filter(Filter::all(
Filter::equal('service.name', $service),
Filter::equal('host.name', $hostname)
));
$this->applyRestrictions($query);
/** @var Service $service */
$service = $query->first();
return ($service !== null);
}
public function getHostLink($title, $hostname, array $attributes = null)
{
return Link::create(
$title,
'icingadb/host',
['name' => $hostname],
$attributes
);
}
public function getHostState($hostname)
{
$hostStates = [
'0' => 'up',
'1' => 'down',
'2' => 'unreachable',
'99' => 'pending',
];
$query = Host::on($this->getDb())->with(['state']);
$query
->setResultSetClass(VolatileStateResults::class)
->filter(Filter::equal('host.name', $hostname));
$this->applyRestrictions($query);
/** @var Host $host */
$host = $query->first();
$result = (object) [
'hostname' => $hostname,
'state' => '99',
'problem' => '0',
'acknowledged' => '0',
'in_downtime' => '0',
'output' => null,
];
if ($host !== null) {
// TODO: implement this for icingadb (function is unused atm)
/**
$query = $this->backend->select()->from('hostStatus', [
'hostname' => 'host_name',
'state' => 'host_state',
'problem' => 'host_problem',
'acknowledged' => 'host_acknowledged',
'in_downtime' => 'host_in_downtime',
'output' => 'host_output',
])->where('host_name', $hostname);
*/
}
$result->state = $hostStates[$result->state];
return $result;
}
}

View File

@ -0,0 +1,110 @@
<?php
namespace Icinga\Module\Director;
use gipfl\IcingaWeb2\Link;
use Icinga\Application\Icinga;
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
class MonitorBackendMonitoring implements MonitorBackend
{
protected $backend;
public function __construct()
{
$app = Icinga::app();
$modules = $app->getModuleManager();
if (!$modules->hasLoaded('monitoring') && $app->isCli()) {
$app->getModuleManager()->loadEnabledModules();
}
if ($modules->hasLoaded('monitoring')) {
$this->backend = MonitoringBackend::instance();
}
}
public function isAvailable()
{
return $this->backend !== null;
}
public function hasHost($hostname)
{
if ($this->backend === null) {
return false;
}
return $this->backend->select()->from('hostStatus', [
'hostname' => 'host_name',
])->where('host_name', $hostname)->fetchOne() === $hostname;
}
public function hasService($hostname, $service)
{
if ($this->backend === null) {
return false;
}
return (array) $this->prepareServiceKeyColumnQuery($hostname, $service)->fetchRow() === [
'hostname' => $hostname,
'service' => $service,
];
}
public function getHostLink($title, $hostname, array $attributes = null)
{
return Link::create(
$title,
'monitoring/host/show',
['host' => $hostname],
$attributes
);
}
public function getHostState($hostname)
{
$hostStates = [
'0' => 'up',
'1' => 'down',
'2' => 'unreachable',
'99' => 'pending',
];
$query = $this->backend->select()->from('hostStatus', [
'hostname' => 'host_name',
'state' => 'host_state',
'problem' => 'host_problem',
'acknowledged' => 'host_acknowledged',
'in_downtime' => 'host_in_downtime',
'output' => 'host_output',
])->where('host_name', $hostname);
$res = $query->fetchRow();
if ($res === false) {
$res = (object) [
'hostname' => $hostname,
'state' => '99',
'problem' => '0',
'acknowledged' => '0',
'in_downtime' => '0',
'output' => null,
];
}
$res->state = $hostStates[$res->state];
return $res;
}
protected function prepareServiceKeyColumnQuery($hostname, $service)
{
return $this->backend
->select()
->from('serviceStatus', [
'hostname' => 'host_name',
'service' => 'service_description',
])
->where('host_name', $hostname)
->where('service_description', $service);
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace Icinga\Module\Director\ProvidedHook\Icingadb;
use Exception;
use Icinga\Application\Config;
use Icinga\Authentication\Auth;
use Icinga\Module\Director\Db;
use Icinga\Module\Director\Backend;
use Icinga\Module\Director\Objects\IcingaHost;
use Icinga\Module\Director\Util;
use Icinga\Module\Icingadb\Hook\HostActionsHook;
use Icinga\Module\Icingadb\Model\Host;
use ipl\Web\Url;
use ipl\Web\Widget\Link;
class HostActions extends HostActionsHook
{
public function getActionsForObject(Host $host): array
{
try {
return $this->getThem($host);
} catch (Exception $e) {
return array();
}
}
protected function getThem(Host $host): array
{
$actions = array();
$db = $this->db();
if (! $db) {
return $actions;
}
$hostname = $host->name;
if (Util::hasPermission('director/inspect')) {
$actions[mt('director', 'Inspect')] = Url::fromPath(
'director/inspect/object',
array('type' => 'host', 'plural' => 'hosts', 'name' => $hostname)
);
}
$allowEdit = false;
if (Util::hasPermission('director/hosts') && IcingaHost::exists($hostname, $db)) {
$allowEdit = true;
}
$auth = Auth::getInstance();
if (Util::hasPermission('director/monitoring/hosts')) {
$backend = new Backend(Backend::ICINGADB);
if ($backend->isAvailable() && $backend->authCanEditHost($auth, $hostname)) {
$allowEdit = IcingaHost::exists($hostname, $db);
}
}
if ($allowEdit) {
$label = mt('director', 'Modify');
$actions[] = new Link(
$label,
Url::fromPath('director/host/edit', [
'name' => $hostname
])
);
}
return $actions;
}
protected function db()
{
$resourceName = Config::module('director')->get('db', 'resource');
if (! $resourceName) {
return false;
}
return Db::fromResourceName($resourceName);
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Icinga\Module\Director\ProvidedHook\Icingadb;
use Icinga\Module\Icingadb\Hook\IcingadbSupportHook;
class IcingadbSupport extends IcingadbSupportHook
{
}

View File

@ -0,0 +1,92 @@
<?php
namespace Icinga\Module\Director\ProvidedHook\Icingadb;
use Exception;
use Icinga\Application\Config;
use Icinga\Authentication\Auth;
use Icinga\Module\Director\Db;
use Icinga\Module\Director\Backend;
use Icinga\Module\Director\Objects\IcingaHost;
use Icinga\Module\Director\Util;
use Icinga\Module\Icingadb\Hook\ServiceActionsHook;
use Icinga\Module\Icingadb\Model\Service;
use ipl\Web\Url;
use ipl\Web\Widget\Link;
class ServiceActions extends ServiceActionsHook
{
public function getActionsForObject(Service $service): array
{
try {
return $this->getThem($service);
} catch (Exception $e) {
die($e);
return [];
}
}
/**
* @param Service $service
* @return array
* @throws \Icinga\Exception\ProgrammingError
*/
protected function getThem(Service $service)
{
$actions = [];
$db = $this->db();
if (! $db) {
return [];
}
$hostname = $service->host->name;
$serviceName = $service->name;
if (Util::hasPermission('director/inspect')) {
$actions[mt('director', 'Inspect')] = Url::fromPath('director/inspect/object', [
'type' => 'service',
'plural' => 'services',
'name' => sprintf(
'%s!%s',
$hostname,
$serviceName
)
]);
}
$title = null;
if (Util::hasPermission('director/hosts')) {
$title = mt('director', 'Modify');
} elseif (Util::hasPermission('director/monitoring/services')) {
$backend = new Backend(Backend::ICINGADB);
if ($backend->isAvailable()
&& $backend->authCanEditService(Auth::getInstance(), $hostname, $serviceName)
) {
$title = mt('director', 'Modify');
}
} elseif (Util::hasPermission('director/monitoring/services-ro')) {
$title = mt('director', 'Configuration');
}
if ($title && IcingaHost::exists($hostname, $db)) {
$actions[] = new Link(
$title,
Url::fromPath('director/host/findservice', [
'name' => $hostname,
'service' => $serviceName
])
);
}
return $actions;
}
protected function db()
{
$resourceName = Config::module('director')->get('db', 'resource');
if (! $resourceName) {
return false;
}
return Db::fromResourceName($resourceName);
}
}

View File

@ -7,7 +7,7 @@ use Icinga\Application\Benchmark;
use Icinga\Data\Paginatable;
use Icinga\Exception\NotFoundError;
use Icinga\Exception\ProgrammingError;
use Icinga\Module\Director\Integration\MonitoringModule\Monitoring;
use Icinga\Module\Director\Backend;
use Icinga\Module\Director\Web\Controller\Extension\CoreApi;
use Icinga\Module\Director\Web\Controller\Extension\DirectorDb;
use Icinga\Module\Director\Web\Controller\Extension\RestApi;
@ -36,8 +36,8 @@ abstract class ActionController extends Controller implements ControlsAndContent
/** @var UrlParams Hint for IDE, somehow does not work in web */
protected $params;
/** @var Monitoring */
private $monitoring;
/** @var Backend */
private $backend;
/**
* @throws SecurityException
@ -240,14 +240,14 @@ abstract class ActionController extends Controller implements ControlsAndContent
}
/**
* @return Monitoring
* @return Backend
*/
protected function monitoring()
protected function backend()
{
if ($this->monitoring === null) {
$this->monitoring = new Monitoring($this->Auth());
if ($this->backend === null) {
$this->backend = new Backend();
}
return $this->monitoring;
return $this->backend;
}
}

View File

@ -66,6 +66,9 @@ use Icinga\Module\Director\ProvidedHook\IcingaDbCubeLinks;
if ($this->getConfig()->get('frontend', 'disabled', 'no') !== 'yes') {
$this->provideHook('monitoring/HostActions');
$this->provideHook('monitoring/ServiceActions');
$this->provideHook('icingadb/HostActions');
$this->provideHook('icingadb/ServiceActions');
$this->provideHook('icingadb/icingadbSupport');
$this->provideHook('cube/Actions', CubeLinks::class);
$this->provideHook('cube/IcingaDbActions', IcingaDbCubeLinks::class);
}