Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prometheus metric charts #43

Merged
merged 30 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a3fd28e
Add module.js file
jrauh01 May 21, 2024
61d7476
Temp
jrauh01 May 21, 2024
d9da8d7
Add ChartJS to project
jrauh01 May 22, 2024
d8182b4
Provide ChartJS and add chart tab
jrauh01 May 22, 2024
483e7f7
Add event listener to draw charts on specific classes
jrauh01 May 22, 2024
d1c6a85
Add classes for chart sizes
jrauh01 May 22, 2024
75ce044
Add class with metric queries
jrauh01 May 22, 2024
0b68170
Rename tab to 'Charts'
jrauh01 May 22, 2024
d9a74ce
Add class for very small charts
jrauh01 May 23, 2024
aec3b6b
Add classes for doughnut charts
jrauh01 May 23, 2024
56d92d4
Add classes for line charts
jrauh01 May 23, 2024
4e67dc2
Add charts on appropriate selectors
jrauh01 May 23, 2024
e5b57eb
Add ChartsController to show charts for Prometheus metrics
jrauh01 May 23, 2024
d0fe77e
Add NavigationList and according css class for an index page
jrauh01 May 23, 2024
4105edc
Use ipl-sql for database queries
jrauh01 May 23, 2024
21809bf
Remove console.log()
jrauh01 May 23, 2024
e52c2f0
Style fixes
jrauh01 May 23, 2024
acade59
Rewrite functions for metric fetching
jrauh01 May 23, 2024
4d992f0
Use YieldAll for database queries and add constants for metric names
jrauh01 May 24, 2024
e98c29b
Update copyright comment and adjust chart size
jrauh01 May 24, 2024
fec78ff
Revise the metric query functions
jrauh01 May 24, 2024
54dfad2
Add charts to pod and node lists
jrauh01 May 24, 2024
f9dd7c5
Use new functions in ChartsController
jrauh01 May 24, 2024
1da0b66
Add option to change real color of doughnut charts
jrauh01 May 24, 2024
275cd2d
Revise chart coloring
jrauh01 May 24, 2024
7ea5091
Style fixes
Jan-Schuppik May 27, 2024
0a8db1c
style fixes <= php 7.3
Jan-Schuppik May 28, 2024
7e53143
wip
lippserd Jun 10, 2024
eb5ba2b
Change `id` to `uuid`
jhoxhaa Jun 17, 2024
71f3614
Don't display header for metrics if there are none
lippserd Jul 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
301 changes: 301 additions & 0 deletions application/controllers/ChartsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
<?php

/* Icinga for Kubernetes Web | (c) 2024 Icinga GmbH | GPLv2 */

namespace Icinga\Module\Kubernetes\Controllers;

use Icinga\Module\Kubernetes\Web\Controller;
use Icinga\Module\Kubernetes\Web\DoughnutChartStates;
use Icinga\Module\Kubernetes\Web\LineChartMinified;
use Icinga\Module\Kubernetes\Web\NavigationList;
use Icinga\Module\Kubernetes\Web\LineChart;
use Icinga\Module\Kubernetes\Web\DoughnutChart;
use Icinga\Module\Kubernetes\Web\DoughnutChartRequestLimit;
use Icinga\Module\Kubernetes\Common\Database;
use Icinga\Module\Kubernetes\Common\Metrics;
use ipl\Html\HtmlElement;
use ipl\Html\Attributes;
use ipl\Html\Text;
use DateTime;
use DateInterval;

class ChartsController extends Controller
{
protected $LAST_1_HOUR = 60 * 60 * 1000;
protected $LAST_12_HOURS = 60 * 60 * 12 * 1000;
protected $LAST_24_HOURS = 60 * 60 * 24 * 1000;

public function indexAction(): void
{
$this->addContent(new NavigationList([
['href' => '/icingaweb2/kubernetes/charts/cluster', 'text' => 'Cluster Metrics'],
['href' => '/icingaweb2/kubernetes/charts/node', 'text' => 'Node Metrics'],
['href' => '/icingaweb2/kubernetes/charts/pod', 'text' => 'Pod Metrics'],
['href' => '/icingaweb2/kubernetes/charts/container', 'text' => 'Container Metrics']
]));
}

public function clusterAction(): void
{
$this->addTitleTab($this->translate('Cluster Metrics'));

$this->addContent(
new HtmlElement(
'a',
new Attributes(['href' => '/icingaweb2/kubernetes/charts']),
new Text('Back to Charts')
)
);

$metrics = new Metrics(Database::connection());

$clusterMetrics = $metrics->getClusterMetrics(
(new DateTime())->sub(new DateInterval('PT12H')),
Metrics::CLUSTER_CPU_USAGE,
Metrics::CLUSTER_MEMORY_USAGE
);

$this->addContent(
new LineChart(
'chart-medium',
implode(', ', $clusterMetrics[Metrics::CLUSTER_CPU_USAGE]),
implode(', ', array_keys($clusterMetrics[Metrics::CLUSTER_CPU_USAGE])),
'CPU Usage',
'#00a8ff'
)
);

$this->addContent(
new LineChart(
'chart-medium',
implode(', ', $clusterMetrics[Metrics::CLUSTER_MEMORY_USAGE]),
implode(', ', array_keys($clusterMetrics[Metrics::CLUSTER_MEMORY_USAGE])),
'Memory Usage',
'#8c7ae6'
)
);

$pods = $metrics->getNumberOfPodsByState(
Metrics::POD_STATE_RUNNING,
Metrics::POD_STATE_PENDING,
Metrics::POD_STATE_FAILED,
Metrics::POD_STATE_SUCCEEDED
);

$this->addContent(
new DoughnutChart(
'chart-small',
implode(
', ',
[
$pods[Metrics::POD_STATE_RUNNING],
$pods[Metrics::POD_STATE_PENDING],
$pods[Metrics::POD_STATE_FAILED],
$pods[Metrics::POD_STATE_SUCCEEDED]
]
),
implode(
', ',
[
$pods[Metrics::POD_STATE_RUNNING] . ' Running',
$pods[Metrics::POD_STATE_PENDING] . ' Pending',
$pods[Metrics::POD_STATE_FAILED] . ' Failed',
$pods[Metrics::POD_STATE_SUCCEEDED] . ' Succeeded'
]
),
'#007bff, #ffc107, #dc3545, #28a745'
)
);

$current = $metrics->getClusterMetrics(
(new DateTime())->sub(new DateInterval('PT2M')),
Metrics::CLUSTER_CPU_USAGE,
Metrics::CLUSTER_MEMORY_USAGE
);

$this->addContent(
new DoughnutChartStates(
'chart-small',
$current[Metrics::CLUSTER_CPU_USAGE][array_key_last($current[Metrics::CLUSTER_CPU_USAGE])],
'CPU Usage',
'#28a745, #ffc107, #dc3545'
)
);

$this->addContent(
new DoughnutChartStates(
'chart-small',
$current[Metrics::CLUSTER_MEMORY_USAGE][array_key_last($current[Metrics::CLUSTER_MEMORY_USAGE])],
'Memory Usage',
'#28a745, #ffc107, #dc3545'
)
);
}

public function nodeAction(): void
{
$this->addTitleTab($this->translate('Node Metrics'));

$this->addContent(
new HtmlElement(
'a',
new Attributes(['href' => '/icingaweb2/kubernetes/charts']),
new Text('Back to Charts')
)
);

$metrics = new Metrics(Database::connection());

$nodeNetworkMetrics = $metrics->getNodesMetrics(
(new DateTime())->sub(new DateInterval('PT1H')),
Metrics::NODE_NETWORK_RECEIVED_BYTES,
Metrics::NODE_NETWORK_TRANSMITTED_BYTES
);


foreach ($nodeNetworkMetrics as $node) {
$this->addContent(
new LineChart(
'chart-medium',
implode(', ', $node[Metrics::NODE_NETWORK_RECEIVED_BYTES])
. '; '
. implode(', ', $node[Metrics::NODE_NETWORK_TRANSMITTED_BYTES]),
implode(', ', array_keys($node[Metrics::NODE_NETWORK_RECEIVED_BYTES])),
'Received Bytes; Transmitted Bytes',
'#593684; #a3367f'
)
);
}
}

public function podAction(): void
{
$this->addTitleTab($this->translate('Pod Metrics'));

$this->addContent(
new HtmlElement(
'a',
new Attributes(['href' => '/icingaweb2/kubernetes/charts']),
new Text('Back to Charts')
)
);

$metrics = new Metrics(Database::connection());

$podMetricsCurrent = $metrics->getPodsMetricsCurrent(
Metrics::POD_CPU_REQUEST,
Metrics::POD_CPU_LIMIT,
Metrics::POD_CPU_USAGE_CORES,
Metrics::POD_MEMORY_REQUEST,
Metrics::POD_MEMORY_LIMIT,
Metrics::POD_MEMORY_USAGE_BYTES
);

$podMetricsPeriod = $metrics->getPodsMetrics(
(new DateTime())->sub(new DateInterval('PT1H')),
Metrics::POD_CPU_USAGE,
Metrics::POD_MEMORY_USAGE
);

$podMetrics = Metrics::mergeMetrics($podMetricsCurrent, $podMetricsPeriod);

$table = new HtmlElement('table', new Attributes(['class' => 'condition-table']));
$table->addHtml(
new HtmlElement(
'thead',
null,
new HtmlElement(
'tr',
null,
new HtmlElement('th', null, new Text('Pod Name')),
new HtmlElement('th', null, new Text('Cpu')),
new HtmlElement('th', null, new Text('Memory')),
new HtmlElement('th', null, new Text('Cpu')),
new HtmlElement('th', null, new Text('Memory'))
)
)
);

$tbody = new HtmlElement('tbody');

foreach ($podMetrics as $pod) {
$tr = new HtmlElement('tr');
$tr->addHtml(new HtmlElement('td', null, new Text($pod['name'])));
$td = new HtmlElement('td', null);

if (isset($pod[Metrics::POD_CPU_LIMIT]) && $pod[Metrics::POD_CPU_REQUEST] < $pod[Metrics::POD_CPU_LIMIT]) {
$td->addHtml(
new DoughnutChartRequestLimit(
'chart-mini',
$pod[Metrics::POD_CPU_REQUEST],
$pod[Metrics::POD_CPU_LIMIT],
$pod[Metrics::POD_CPU_USAGE_CORES]
)
);
}

$tr->addHtml($td);
$td = new HtmlElement('td', null);

if (
isset($pod[Metrics::POD_MEMORY_LIMIT])
&& $pod[Metrics::POD_MEMORY_REQUEST] < $pod[Metrics::POD_MEMORY_LIMIT]
) {
$td->addHtml(
new DoughnutChartRequestLimit(
'chart-mini',
$pod[Metrics::POD_MEMORY_REQUEST],
$pod[Metrics::POD_MEMORY_LIMIT],
$pod[Metrics::POD_MEMORY_USAGE_BYTES]
)
);
}

$tr->addHtml($td);
$td = new HtmlElement('td', null);

if (isset($pod[Metrics::POD_CPU_USAGE])) {
$td->addHtml(
new LineChartMinified(
'chart-mini',
implode(', ', $pod[Metrics::POD_CPU_USAGE]),
implode(', ', array_keys($pod[Metrics::POD_CPU_USAGE])),
'#00a8ff'
)
);
}

$tr->addHtml($td);
$td = new HtmlElement('td', null);

if (isset($pod[Metrics::POD_MEMORY_USAGE])) {
$td->addHtml(
new LineChartMinified(
'chart-mini',
implode(', ', $pod[Metrics::POD_MEMORY_USAGE]),
implode(', ', array_keys($pod[Metrics::POD_MEMORY_USAGE])),
'#ffa800'
)
);
}
$tr->addHtml($td);
$tbody->addHtml($tr);
}

$table->addHtml($tbody);
$this->addContent($table);
}

public function containerAction(): void
{
$this->addTitleTab($this->translate('Container Metrics'));

$this->addContent(
new HtmlElement(
'a',
new Attributes(['href' => '/icingaweb2/kubernetes/charts']),
new Text('Back to Charts')
)
);
}
}
11 changes: 11 additions & 0 deletions configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,15 @@
]
);

$section->add(
N_('Charts'),
[
'description' => $this->translate('Charts'),
'url' => 'kubernetes/charts',
'priority' => $priority++
]
);

$this->provideConfigTab(
'database',
[
Expand All @@ -167,3 +176,5 @@
'url' => 'config/database'
]
);

$this->provideJsFile('vendor/chart.umd.js');
Loading
Loading