From 1c6090193dcea4e97ff844ffe2d8745fafd5b9e4 Mon Sep 17 00:00:00 2001 From: Tobias Tiederle Date: Wed, 12 Oct 2022 15:08:55 +0000 Subject: [PATCH] add support for icingadb as only icingaweb2 data backend --- application/controllers/HostController.php | 27 ++-- application/controllers/ServiceController.php | 6 +- library/Director/Backend.php | 151 ++++++++++++++++++ library/Director/MonitorBackend.php | 16 ++ library/Director/MonitorBackendIcingadb.php | 111 +++++++++++++ library/Director/MonitorBackendMonitoring.php | 110 +++++++++++++ .../ProvidedHook/Icingadb/HostActions.php | 77 +++++++++ .../ProvidedHook/Icingadb/IcingadbSupport.php | 10 ++ .../ProvidedHook/Icingadb/ServiceActions.php | 92 +++++++++++ .../Web/Controller/ActionController.php | 16 +- register-hooks.php | 3 + 11 files changed, 597 insertions(+), 22 deletions(-) create mode 100644 library/Director/Backend.php create mode 100644 library/Director/MonitorBackend.php create mode 100644 library/Director/MonitorBackendIcingadb.php create mode 100644 library/Director/MonitorBackendMonitoring.php create mode 100644 library/Director/ProvidedHook/Icingadb/HostActions.php create mode 100644 library/Director/ProvidedHook/Icingadb/IcingadbSupport.php create mode 100644 library/Director/ProvidedHook/Icingadb/ServiceActions.php diff --git a/application/controllers/HostController.php b/application/controllers/HostController.php index 51056990..a23da138 100644 --- a/application/controllers/HostController.php +++ b/application/controllers/HostController.php @@ -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(); diff --git a/application/controllers/ServiceController.php b/application/controllers/ServiceController.php index a24b5050..5d2f3d1f 100644 --- a/application/controllers/ServiceController.php +++ b/application/controllers/ServiceController.php @@ -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'); diff --git a/library/Director/Backend.php b/library/Director/Backend.php new file mode 100644 index 00000000..8aac0fc4 --- /dev/null +++ b/library/Director/Backend.php @@ -0,0 +1,151 @@ +null 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); + } + } +} diff --git a/library/Director/MonitorBackend.php b/library/Director/MonitorBackend.php new file mode 100644 index 00000000..95c3f123 --- /dev/null +++ b/library/Director/MonitorBackend.php @@ -0,0 +1,16 @@ +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; + } +} diff --git a/library/Director/MonitorBackendMonitoring.php b/library/Director/MonitorBackendMonitoring.php new file mode 100644 index 00000000..de91b8dd --- /dev/null +++ b/library/Director/MonitorBackendMonitoring.php @@ -0,0 +1,110 @@ +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); + } +} diff --git a/library/Director/ProvidedHook/Icingadb/HostActions.php b/library/Director/ProvidedHook/Icingadb/HostActions.php new file mode 100644 index 00000000..8bcc8638 --- /dev/null +++ b/library/Director/ProvidedHook/Icingadb/HostActions.php @@ -0,0 +1,77 @@ +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); + } +} diff --git a/library/Director/ProvidedHook/Icingadb/IcingadbSupport.php b/library/Director/ProvidedHook/Icingadb/IcingadbSupport.php new file mode 100644 index 00000000..5a593046 --- /dev/null +++ b/library/Director/ProvidedHook/Icingadb/IcingadbSupport.php @@ -0,0 +1,10 @@ +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); + } +} diff --git a/library/Director/Web/Controller/ActionController.php b/library/Director/Web/Controller/ActionController.php index a281e170..1740f849 100644 --- a/library/Director/Web/Controller/ActionController.php +++ b/library/Director/Web/Controller/ActionController.php @@ -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; } } diff --git a/register-hooks.php b/register-hooks.php index 62fd5f51..36e7e701 100644 --- a/register-hooks.php +++ b/register-hooks.php @@ -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); }