params->shiftRequired('name'); $query = Host::on($this->getDb())->with(['state', 'icon_image', 'timeperiod']); $query ->setResultSetClass(VolatileStateResults::class) ->filter(Filter::equal('host.name', $name)); $this->applyRestrictions($query); /** @var Host $host */ $host = $query->first(); if ($host === null) { throw new NotFoundError(t('Host not found')); } $this->host = $host; $this->loadTabsForObject($host); $this->addControl((new HostList([$host])) ->setViewMode('objectHeader') ->setDetailActionsDisabled() ->setNoSubjectLink()); $this->setTitleTab($this->getRequest()->getActionName()); $this->setTitle($host->display_name); } public function indexAction(): void { $serviceSummary = ServicestateSummary::on($this->getDb()); $serviceSummary->filter(Filter::equal('service.host_id', $this->host->id)); $this->applyRestrictions($serviceSummary); if ($this->host->state->is_overdue) { $this->controls->addAttributes(['class' => 'overdue']); } $this->addControl(new HostMetaInfo($this->host)); $this->addControl(new QuickActions($this->host)); $this->addContent(new HostDetail($this->host, $serviceSummary->first())); $this->setAutorefreshInterval(10); } public function sourceAction(): void { $this->assertPermission('icingadb/object/show-source'); $apiResult = (new CommandTransport())->send( (new GetObjectCommand()) ->setObjects(new ArrayIterator([$this->host])) ); if ($this->host->state->is_overdue) { $this->controls->addAttributes(['class' => 'overdue']); } $this->addContent(new HostInspectionDetail( $this->host, reset($apiResult) )); } public function historyAction(): Generator { $compact = $this->view->compact; // TODO: Find a less-legacy way.. if ($this->host->state->is_overdue) { $this->controls->addAttributes(['class' => 'overdue']); } $db = $this->getDb(); $history = History::on($db)->with([ 'host', 'host.state', 'comment', 'downtime', 'flapping', 'notification', 'acknowledgement', 'state' ]); $history->filter(Filter::all( Filter::equal('history.host_id', $this->host->id), Filter::unlike('history.service_id', '*') )); $before = $this->params->shift('before', time()); $url = Url::fromRequest()->setParams(clone $this->params); $limitControl = $this->createLimitControl(); $paginationControl = $this->createPaginationControl($history); $sortControl = $this->createSortControl( $history, [ 'history.event_time desc, history.event_type desc' => t('Event Time') ] ); $viewModeSwitcher = $this->createViewModeSwitcher($paginationControl, $limitControl, true); $history->peekAhead(); $page = $paginationControl->getCurrentPageNumber(); if ($page > 1 && ! $compact) { $history->limit($page * $limitControl->getLimit()); } $history->filter(Filter::lessThanOrEqual('event_time', $before)); yield $this->export($history); $this->addControl($sortControl); $this->addControl($limitControl); $this->addControl($viewModeSwitcher); $historyList = (new HistoryList($history->execute())) ->setViewMode($viewModeSwitcher->getViewMode()) ->setPageSize($limitControl->getLimit()) ->setLoadMoreUrl($url->setParam('before', $before)); if ($compact) { $historyList->setPageNumber($page); } if ($compact && $page > 1) { $this->document->addFrom($historyList); } else { $this->addContent($historyList); } } public function servicesAction(): Generator { if ($this->host->state->is_overdue) { $this->controls->addAttributes(['class' => 'overdue']); } $db = $this->getDb(); $services = Service::on($db)->with([ 'state', 'state.last_comment', 'icon_image', 'host', 'host.state' ]); $services ->setResultSetClass(VolatileStateResults::class) ->filter(Filter::equal('host.id', $this->host->id)); $this->applyRestrictions($services); $limitControl = $this->createLimitControl(); $paginationControl = $this->createPaginationControl($services); $viewModeSwitcher = $this->createViewModeSwitcher($paginationControl, $limitControl); $sortControl = $this->createSortControl( $services, [ 'service.display_name' => t('Name'), 'service.state.severity desc,service.state.last_state_change desc' => t('Severity'), 'service.state.soft_state' => t('Current State'), 'service.state.last_state_change desc' => t('Last State Change') ] ); yield $this->export($services); $serviceList = (new ServiceList($services)) ->setViewMode($viewModeSwitcher->getViewMode()); $this->addControl($paginationControl); $this->addControl($sortControl); $this->addControl($limitControl); $this->addControl($viewModeSwitcher); $this->addContent($serviceList); $this->setAutorefreshInterval(10); } public function parentsAction(): Generator { $nodesQuery = $this->fetchNodes(true); $limitControl = $this->createLimitControl(); $paginationControl = $this->createPaginationControl($nodesQuery); $sortControl = $this->createSortControl( $nodesQuery, [ 'name' => $this->translate('Name'), 'severity desc, last_state_change desc' => $this->translate('Severity'), 'state' => $this->translate('Current State'), 'last_state_change desc' => $this->translate('Last State Change') ] ); $viewModeSwitcher = $this->createViewModeSwitcher($paginationControl, $limitControl); $searchBar = $this->createSearchBar( $nodesQuery, [ $limitControl->getLimitParam(), $sortControl->getSortParam(), $viewModeSwitcher->getViewModeParam(), 'name' ] ); if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { if ($searchBar->hasBeenSubmitted()) { $filter = $this->getFilter(); } else { $this->addControl($searchBar); $this->sendMultipartUpdate(); return; } } else { $filter = $searchBar->getFilter(); } $nodesQuery->filter($filter); yield $this->export($nodesQuery); $this->addControl($paginationControl); $this->addControl($sortControl); $this->addControl($limitControl); $this->addControl($viewModeSwitcher); $this->addControl($searchBar); $this->addContent( (new DependencyNodeList($nodesQuery)) ->setViewMode($viewModeSwitcher->getViewMode()) ); if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { $this->sendMultipartUpdate(); } $this->setAutorefreshInterval(10); } public function childrenAction(): Generator { $nodesQuery = $this->fetchNodes(); $limitControl = $this->createLimitControl(); $paginationControl = $this->createPaginationControl($nodesQuery); $sortControl = $this->createSortControl( $nodesQuery, [ 'name' => $this->translate('Name'), 'severity desc, last_state_change desc' => $this->translate('Severity'), 'state' => $this->translate('Current State'), 'last_state_change desc' => $this->translate('Last State Change') ] ); $viewModeSwitcher = $this->createViewModeSwitcher($paginationControl, $limitControl); $searchBar = $this->createSearchBar( $nodesQuery, [ $limitControl->getLimitParam(), $sortControl->getSortParam(), $viewModeSwitcher->getViewModeParam(), 'name' ] ); $searchBar->getSuggestionUrl()->setParam('isChildrenTab'); $searchBar->getEditorUrl()->setParam('isChildrenTab'); if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { if ($searchBar->hasBeenSubmitted()) { $filter = $this->getFilter(); } else { $this->addControl($searchBar); $this->sendMultipartUpdate(); return; } } else { $filter = $searchBar->getFilter(); } $nodesQuery->filter($filter); yield $this->export($nodesQuery); $this->addControl($paginationControl); $this->addControl($sortControl); $this->addControl($limitControl); $this->addControl($viewModeSwitcher); $this->addControl($searchBar); $this->addContent( (new DependencyNodeList($nodesQuery)) ->setViewMode($viewModeSwitcher->getViewMode()) ); if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { $this->sendMultipartUpdate(); } $this->setAutorefreshInterval(10); } public function completeAction(): void { $isChildrenTab = $this->params->shift('isChildrenTab'); $relation = $isChildrenTab ? 'parent' : 'child'; $suggestions = (new ObjectSuggestions()) ->setModel(DependencyNode::class) ->setBaseFilter(Filter::equal("$relation.host.id", $this->host->id)) ->forRequest($this->getServerRequest()); $this->getDocument()->add($suggestions); } public function searchEditorAction(): void { $isChildrenTab = $this->params->shift('isChildrenTab'); $redirectUrl = $isChildrenTab ? Url::fromPath('icingadb/host/children', ['name' => $this->host->name]) : Url::fromPath('icingadb/host/parents', ['name' => $this->host->name]); $editor = $this->createSearchEditor( DependencyNode::on($this->getDb()), $redirectUrl, [ LimitControl::DEFAULT_LIMIT_PARAM, SortControl::DEFAULT_SORT_PARAM, ViewModeSwitcher::DEFAULT_VIEW_MODE_PARAM, 'name' ] ); if ($isChildrenTab) { $editor->getSuggestionUrl()->setParam('isChildrenTab'); } $this->getDocument()->add($editor); $this->setTitle($this->translate('Adjust Filter')); } /** * Fetch the nodes for the current host * * @param bool $fetchParents Whether to fetch the parents or the children * * @return Query */ protected function fetchNodes(bool $fetchParents = false): Query { $query = DependencyNode::on($this->getDb()) ->with([ 'host', 'host.state', 'host.state.last_comment', 'service', 'service.state', 'service.state.last_comment', 'service.host', 'service.host.state', 'redundancy_group', 'redundancy_group.state' ]) ->setResultSetClass(VolatileStateResults::class); $this->joinFix($query, $this->host->id, $fetchParents); $this->applyRestrictions($query); return $query; } protected function createTabs(): Tabs { if (! Backend::supportsDependencies()) { $hasDependencyNode = false; } else { $hasDependencyNode = DependencyNode::on($this->getDb()) ->columns([new Expression('1')]) ->filter(Filter::all( Filter::equal('host_id', $this->host->id), Filter::unlike('service_id', '*') )) ->first() !== null; } $tabs = $this->getTabs() ->add('index', [ 'label' => t('Host'), 'url' => Links::host($this->host) ]) ->add('services', [ 'label' => t('Services'), 'url' => HostLinks::services($this->host) ]) ->add('history', [ 'label' => t('History'), 'url' => HostLinks::history($this->host) ]); if ($hasDependencyNode) { $tabs->add('parents', [ 'label' => $this->translate('Parents'), 'url' => Url::fromPath('icingadb/host/parents', ['name' => $this->host->name]) ])->add('children', [ 'label' => $this->translate('Children'), 'url' => Url::fromPath('icingadb/host/children', ['name' => $this->host->name]) ]); } if ($this->hasPermission('icingadb/object/show-source')) { $tabs->add('source', [ 'label' => t('Source'), 'url' => Links::hostSource($this->host) ]); } foreach ($this->loadAdditionalTabs() as $name => $tab) { $tabs->add($name, $tab + ['urlParams' => ['name' => $this->host->name]]); } return $tabs; } protected function setTitleTab(string $name): void { $tab = $this->createTabs()->get($name); if ($tab !== null) { $this->getTabs()->activate($name); } } protected function fetchCommandTargets(): array { return [$this->host]; } protected function getCommandTargetsUrl(): Url { return Links::host($this->host); } protected function getDefaultTabControls(): array { return [(new HostList([$this->host]))->setDetailActionsDisabled()->setNoSubjectLink()]; } /** * Filter the query to only include (direct) parents or children of the given object. * * @todo This is a workaround, remove it once https://github.com/Icinga/ipl-orm/issues/76 is fixed * * @param Query $query * @param string $objectId * @param bool $fetchParents Fetch parents if true, children otherwise */ protected function joinFix(Query $query, string $objectId, bool $fetchParents = false): void { $filterTable = $fetchParents ? 'child' : 'parent'; $utilizeType = $fetchParents ? 'parent' : 'child'; $edge = DependencyEdge::on($this->getDb()) ->utilize($utilizeType) ->columns([new Expression('1')]) ->filter(Filter::equal("$filterTable.host.id", $objectId)) ->filter(Filter::unlike("$filterTable.service.id", '*')); $edge->getFilter()->metaData()->set('forceOptimization', false); $resolver = $edge->getResolver(); $edgeAlias = $resolver->getAlias( $resolver->resolveRelation($resolver->qualifyPath($utilizeType, $edge->getModel()->getTableName())) ->getTarget() ); $query->filter(new Exists( $edge->assembleSelect() ->where( "$edgeAlias.id = " . $query->getResolver()->qualifyColumn('id', $query->getModel()->getTableName()) ) )); } }