12
12
use Icinga \Module \Icingadb \Common \HostLinks ;
13
13
use Icinga \Module \Icingadb \Common \Links ;
14
14
use Icinga \Module \Icingadb \Hook \TabHook \HookActions ;
15
+ use Icinga \Module \Icingadb \Model \DependencyEdge ;
16
+ use Icinga \Module \Icingadb \Model \DependencyNode ;
15
17
use Icinga \Module \Icingadb \Model \History ;
16
18
use Icinga \Module \Icingadb \Model \Host ;
17
19
use Icinga \Module \Icingadb \Model \Service ;
18
20
use Icinga \Module \Icingadb \Model \ServicestateSummary ;
19
21
use Icinga \Module \Icingadb \Redis \VolatileStateResults ;
22
+ use Icinga \Module \Icingadb \Web \Control \SearchBar \ObjectSuggestions ;
23
+ use Icinga \Module \Icingadb \Web \Control \ViewModeSwitcher ;
20
24
use Icinga \Module \Icingadb \Web \Controller ;
21
25
use Icinga \Module \Icingadb \Widget \Detail \HostDetail ;
22
26
use Icinga \Module \Icingadb \Widget \Detail \HostInspectionDetail ;
23
27
use Icinga \Module \Icingadb \Widget \Detail \HostMetaInfo ;
24
28
use Icinga \Module \Icingadb \Widget \Detail \QuickActions ;
29
+ use Icinga \Module \Icingadb \Widget \ItemList \DependencyNodeList ;
25
30
use Icinga \Module \Icingadb \Widget \ItemList \HostList ;
26
31
use Icinga \Module \Icingadb \Widget \ItemList \HistoryList ;
27
32
use Icinga \Module \Icingadb \Widget \ItemList \ServiceList ;
33
+ use ipl \Orm \Query ;
34
+ use ipl \Sql \Expression ;
35
+ use ipl \Sql \Filter \Exists ;
28
36
use ipl \Stdlib \Filter ;
37
+ use ipl \Web \Control \LimitControl ;
38
+ use ipl \Web \Control \SortControl ;
29
39
use ipl \Web \Url ;
30
40
use ipl \Web \Widget \Tabs ;
31
41
@@ -224,8 +234,207 @@ public function servicesAction()
224
234
$ this ->setAutorefreshInterval (10 );
225
235
}
226
236
237
+ public function parentsAction ()
238
+ {
239
+ $ nodesQuery = $ this ->fetchNodes (true );
240
+
241
+ $ limitControl = $ this ->createLimitControl ();
242
+ $ paginationControl = $ this ->createPaginationControl ($ nodesQuery );
243
+ $ sortControl = $ this ->createSortControl (
244
+ $ nodesQuery ,
245
+ [
246
+ 'name ' => $ this ->translate ('Name ' ),
247
+ 'severity desc, last_state_change desc ' => $ this ->translate ('Severity ' ),
248
+ 'state ' => $ this ->translate ('Current State ' ),
249
+ 'last_state_change desc ' => $ this ->translate ('Last State Change ' )
250
+ ]
251
+ );
252
+ $ viewModeSwitcher = $ this ->createViewModeSwitcher ($ paginationControl , $ limitControl );
253
+
254
+ $ searchBar = $ this ->createSearchBar (
255
+ $ nodesQuery ,
256
+ [
257
+ $ limitControl ->getLimitParam (),
258
+ $ sortControl ->getSortParam (),
259
+ $ viewModeSwitcher ->getViewModeParam (),
260
+ 'name '
261
+ ]
262
+ );
263
+
264
+ if ($ searchBar ->hasBeenSent () && ! $ searchBar ->isValid ()) {
265
+ if ($ searchBar ->hasBeenSubmitted ()) {
266
+ $ filter = $ this ->getFilter ();
267
+ } else {
268
+ $ this ->addControl ($ searchBar );
269
+ $ this ->sendMultipartUpdate ();
270
+ return ;
271
+ }
272
+ } else {
273
+ $ filter = $ searchBar ->getFilter ();
274
+ }
275
+
276
+ $ nodesQuery ->filter ($ filter );
277
+
278
+ $ this ->addControl ($ paginationControl );
279
+ $ this ->addControl ($ sortControl );
280
+ $ this ->addControl ($ limitControl );
281
+ $ this ->addControl ($ viewModeSwitcher );
282
+ $ this ->addControl ($ searchBar );
283
+
284
+ $ this ->addContent (
285
+ (new DependencyNodeList ($ nodesQuery ))
286
+ ->setViewMode ($ viewModeSwitcher ->getViewMode ())
287
+ );
288
+
289
+ if (! $ searchBar ->hasBeenSubmitted () && $ searchBar ->hasBeenSent ()) {
290
+ $ this ->sendMultipartUpdate ();
291
+ }
292
+
293
+ $ this ->setAutorefreshInterval (10 );
294
+ }
295
+
296
+ public function childrenAction ()
297
+ {
298
+ $ nodesQuery = $ this ->fetchNodes ();
299
+
300
+ $ limitControl = $ this ->createLimitControl ();
301
+ $ paginationControl = $ this ->createPaginationControl ($ nodesQuery );
302
+ $ sortControl = $ this ->createSortControl (
303
+ $ nodesQuery ,
304
+ [
305
+ 'name ' => t ('Name ' ),
306
+ 'severity desc, last_state_change desc ' => t ('Severity ' ),
307
+ 'state ' => t ('Current State ' ),
308
+ 'last_state_change desc ' => t ('Last State Change ' )
309
+ ]
310
+ );
311
+ $ viewModeSwitcher = $ this ->createViewModeSwitcher ($ paginationControl , $ limitControl );
312
+
313
+ $ searchBar = $ this ->createSearchBar (
314
+ $ nodesQuery ,
315
+ [
316
+ $ limitControl ->getLimitParam (),
317
+ $ sortControl ->getSortParam (),
318
+ $ viewModeSwitcher ->getViewModeParam (),
319
+ 'name '
320
+ ]
321
+ );
322
+
323
+ $ searchBar ->getSuggestionUrl ()->setParam ('isChildrenTab ' );
324
+ $ searchBar ->getEditorUrl ()->setParam ('isChildrenTab ' );
325
+
326
+ if ($ searchBar ->hasBeenSent () && ! $ searchBar ->isValid ()) {
327
+ if ($ searchBar ->hasBeenSubmitted ()) {
328
+ $ filter = $ this ->getFilter ();
329
+ } else {
330
+ $ this ->addControl ($ searchBar );
331
+ $ this ->sendMultipartUpdate ();
332
+ return ;
333
+ }
334
+ } else {
335
+ $ filter = $ searchBar ->getFilter ();
336
+ }
337
+
338
+ $ nodesQuery ->filter ($ filter );
339
+
340
+ $ this ->addControl ($ paginationControl );
341
+ $ this ->addControl ($ sortControl );
342
+ $ this ->addControl ($ limitControl );
343
+ $ this ->addControl ($ viewModeSwitcher );
344
+ $ this ->addControl ($ searchBar );
345
+
346
+ $ this ->addContent (
347
+ (new DependencyNodeList ($ nodesQuery ))
348
+ ->setViewMode ($ viewModeSwitcher ->getViewMode ())
349
+ );
350
+
351
+ if (! $ searchBar ->hasBeenSubmitted () && $ searchBar ->hasBeenSent ()) {
352
+ $ this ->sendMultipartUpdate ();
353
+ }
354
+
355
+ $ this ->setAutorefreshInterval (10 );
356
+ }
357
+
358
+ public function completeAction (): void
359
+ {
360
+ $ isChildrenTab = $ this ->params ->shift ('isChildrenTab ' );
361
+ $ relation = $ isChildrenTab ? 'parent ' : 'child ' ;
362
+
363
+ $ suggestions = (new ObjectSuggestions ())
364
+ ->setModel (DependencyNode::class)
365
+ ->setBaseFilter (Filter::equal ("$ relation.host.id " , $ this ->host ->id ))
366
+ ->forRequest ($ this ->getServerRequest ());
367
+
368
+ $ this ->getDocument ()->add ($ suggestions );
369
+ }
370
+
371
+ public function searchEditorAction (): void
372
+ {
373
+ $ isChildrenTab = $ this ->params ->shift ('isChildrenTab ' );
374
+ $ redirectUrl = $ isChildrenTab
375
+ ? Url::fromPath ('icingadb/host/children ' , ['name ' => $ this ->host ->name ])
376
+ : Url::fromPath ('icingadb/host/parents ' , ['name ' => $ this ->host ->name ]);
377
+
378
+ $ editor = $ this ->createSearchEditor (
379
+ DependencyNode::on ($ this ->getDb ()),
380
+ $ redirectUrl ,
381
+ [
382
+ LimitControl::DEFAULT_LIMIT_PARAM ,
383
+ SortControl::DEFAULT_SORT_PARAM ,
384
+ ViewModeSwitcher::DEFAULT_VIEW_MODE_PARAM ,
385
+ 'name '
386
+ ]
387
+ );
388
+
389
+ if ($ isChildrenTab ) {
390
+ $ editor ->getSuggestionUrl ()->setParam ('isChildrenTab ' );
391
+ }
392
+
393
+ $ this ->getDocument ()->add ($ editor );
394
+ $ this ->setTitle ($ this ->translate ('Adjust Filter ' ));
395
+ }
396
+
397
+ /**
398
+ * Fetch the nodes for the current host
399
+ *
400
+ * @param bool $fetchParents Whether to fetch the parents or the children
401
+ *
402
+ * @return Query
403
+ */
404
+ protected function fetchNodes (bool $ fetchParents = false ): Query
405
+ {
406
+ $ query = DependencyNode::on ($ this ->getDb ())
407
+ ->with ([
408
+ 'host ' ,
409
+ 'host.state ' ,
410
+ 'host.state.last_comment ' ,
411
+ 'service ' ,
412
+ 'service.state ' ,
413
+ 'service.state.last_comment ' ,
414
+ 'service.host ' ,
415
+ 'service.host.state ' ,
416
+ 'redundancy_group ' ,
417
+ 'redundancy_group.state '
418
+ ])
419
+ ->setResultSetClass (VolatileStateResults::class);
420
+
421
+ $ this ->joinFix ($ query , $ this ->host ->id , $ fetchParents );
422
+
423
+ $ this ->applyRestrictions ($ query );
424
+
425
+ return $ query ;
426
+ }
427
+
227
428
protected function createTabs (): Tabs
228
429
{
430
+ $ hasDependencyNode = DependencyNode::on ($ this ->getDb ())
431
+ ->columns ('1 ' )
432
+ ->filter (Filter::all (
433
+ Filter::equal ('host_id ' , $ this ->host ->id ),
434
+ Filter::unlike ('service_id ' , '* ' )
435
+ ))
436
+ ->first () !== null ;
437
+
229
438
$ tabs = $ this ->getTabs ()
230
439
->add ('index ' , [
231
440
'label ' => t ('Host ' ),
@@ -236,10 +445,20 @@ protected function createTabs(): Tabs
236
445
'url ' => HostLinks::services ($ this ->host )
237
446
])
238
447
->add ('history ' , [
239
- 'label ' => t ('History ' ),
240
- 'url ' => HostLinks::history ($ this ->host )
448
+ 'label ' => t ('History ' ),
449
+ 'url ' => HostLinks::history ($ this ->host )
241
450
]);
242
451
452
+ if ($ hasDependencyNode ) {
453
+ $ tabs ->add ('parents ' , [
454
+ 'label ' => t ('Parents ' ),
455
+ 'url ' => Url::fromPath ('icingadb/host/parents ' , ['name ' => $ this ->host ->name ])
456
+ ])->add ('children ' , [
457
+ 'label ' => t ('Children ' ),
458
+ 'url ' => Url::fromPath ('icingadb/host/children ' , ['name ' => $ this ->host ->name ])
459
+ ]);
460
+ }
461
+
243
462
if ($ this ->hasPermission ('icingadb/object/show-source ' )) {
244
463
$ tabs ->add ('source ' , [
245
464
'label ' => t ('Source ' ),
@@ -279,4 +498,42 @@ protected function getDefaultTabControls(): array
279
498
{
280
499
return [(new HostList ([$ this ->host ]))->setDetailActionsDisabled ()->setNoSubjectLink ()];
281
500
}
501
+
502
+ /**
503
+ * Filter the query to only include (direct) parents or children of the given object.
504
+ *
505
+ * @todo This is a workaround, remove it once https://github.com/Icinga/ipl-orm/issues/76 is fixed
506
+ *
507
+ * @param Query $query
508
+ * @param string $objectId
509
+ * @param bool $fetchParents Fetch parents if true, children otherwise
510
+ */
511
+ protected function joinFix (Query $ query , string $ objectId , bool $ fetchParents = false ): void
512
+ {
513
+ $ filterTable = $ fetchParents ? 'child ' : 'parent ' ;
514
+ $ utilizeType = $ fetchParents ? 'parent ' : 'child ' ;
515
+
516
+ $ edge = DependencyEdge::on ($ this ->getDb ())
517
+ ->utilize ($ utilizeType )
518
+ ->columns ([new Expression ('1 ' )])
519
+ ->filter (Filter::equal ("$ filterTable.host.id " , $ objectId ))
520
+ ->filter (Filter::unlike ("$ filterTable.service.id " , '* ' ));
521
+
522
+ $ edge ->getFilter ()->metaData ()->set ('forceOptimization ' , false );
523
+
524
+ $ resolver = $ edge ->getResolver ();
525
+
526
+ $ edgeAlias = $ resolver ->getAlias (
527
+ $ resolver ->resolveRelation ($ resolver ->qualifyPath ($ utilizeType , $ edge ->getModel ()->getTableName ()))
528
+ ->getTarget ()
529
+ );
530
+
531
+ $ query ->filter (new Exists (
532
+ $ edge ->assembleSelect ()
533
+ ->where (
534
+ "$ edgeAlias.id = "
535
+ . $ query ->getResolver ()->qualifyColumn ('id ' , $ query ->getModel ()->getTableName ())
536
+ )
537
+ ));
538
+ }
282
539
}
0 commit comments