Skip to content

Commit 8bae62a

Browse files
authored
Merge pull request #43 from Icinga/metric-charts
Prometheus metric charts
2 parents 85c0cb6 + 71f3614 commit 8bae62a

18 files changed

+1595
-32
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
<?php
2+
3+
/* Icinga for Kubernetes Web | (c) 2024 Icinga GmbH | GPLv2 */
4+
5+
namespace Icinga\Module\Kubernetes\Controllers;
6+
7+
use Icinga\Module\Kubernetes\Web\Controller;
8+
use Icinga\Module\Kubernetes\Web\DoughnutChartStates;
9+
use Icinga\Module\Kubernetes\Web\LineChartMinified;
10+
use Icinga\Module\Kubernetes\Web\NavigationList;
11+
use Icinga\Module\Kubernetes\Web\LineChart;
12+
use Icinga\Module\Kubernetes\Web\DoughnutChart;
13+
use Icinga\Module\Kubernetes\Web\DoughnutChartRequestLimit;
14+
use Icinga\Module\Kubernetes\Common\Database;
15+
use Icinga\Module\Kubernetes\Common\Metrics;
16+
use ipl\Html\HtmlElement;
17+
use ipl\Html\Attributes;
18+
use ipl\Html\Text;
19+
use DateTime;
20+
use DateInterval;
21+
22+
class ChartsController extends Controller
23+
{
24+
protected $LAST_1_HOUR = 60 * 60 * 1000;
25+
protected $LAST_12_HOURS = 60 * 60 * 12 * 1000;
26+
protected $LAST_24_HOURS = 60 * 60 * 24 * 1000;
27+
28+
public function indexAction(): void
29+
{
30+
$this->addContent(new NavigationList([
31+
['href' => '/icingaweb2/kubernetes/charts/cluster', 'text' => 'Cluster Metrics'],
32+
['href' => '/icingaweb2/kubernetes/charts/node', 'text' => 'Node Metrics'],
33+
['href' => '/icingaweb2/kubernetes/charts/pod', 'text' => 'Pod Metrics'],
34+
['href' => '/icingaweb2/kubernetes/charts/container', 'text' => 'Container Metrics']
35+
]));
36+
}
37+
38+
public function clusterAction(): void
39+
{
40+
$this->addTitleTab($this->translate('Cluster Metrics'));
41+
42+
$this->addContent(
43+
new HtmlElement(
44+
'a',
45+
new Attributes(['href' => '/icingaweb2/kubernetes/charts']),
46+
new Text('Back to Charts')
47+
)
48+
);
49+
50+
$metrics = new Metrics(Database::connection());
51+
52+
$clusterMetrics = $metrics->getClusterMetrics(
53+
(new DateTime())->sub(new DateInterval('PT12H')),
54+
Metrics::CLUSTER_CPU_USAGE,
55+
Metrics::CLUSTER_MEMORY_USAGE
56+
);
57+
58+
$this->addContent(
59+
new LineChart(
60+
'chart-medium',
61+
implode(', ', $clusterMetrics[Metrics::CLUSTER_CPU_USAGE]),
62+
implode(', ', array_keys($clusterMetrics[Metrics::CLUSTER_CPU_USAGE])),
63+
'CPU Usage',
64+
'#00a8ff'
65+
)
66+
);
67+
68+
$this->addContent(
69+
new LineChart(
70+
'chart-medium',
71+
implode(', ', $clusterMetrics[Metrics::CLUSTER_MEMORY_USAGE]),
72+
implode(', ', array_keys($clusterMetrics[Metrics::CLUSTER_MEMORY_USAGE])),
73+
'Memory Usage',
74+
'#8c7ae6'
75+
)
76+
);
77+
78+
$pods = $metrics->getNumberOfPodsByState(
79+
Metrics::POD_STATE_RUNNING,
80+
Metrics::POD_STATE_PENDING,
81+
Metrics::POD_STATE_FAILED,
82+
Metrics::POD_STATE_SUCCEEDED
83+
);
84+
85+
$this->addContent(
86+
new DoughnutChart(
87+
'chart-small',
88+
implode(
89+
', ',
90+
[
91+
$pods[Metrics::POD_STATE_RUNNING],
92+
$pods[Metrics::POD_STATE_PENDING],
93+
$pods[Metrics::POD_STATE_FAILED],
94+
$pods[Metrics::POD_STATE_SUCCEEDED]
95+
]
96+
),
97+
implode(
98+
', ',
99+
[
100+
$pods[Metrics::POD_STATE_RUNNING] . ' Running',
101+
$pods[Metrics::POD_STATE_PENDING] . ' Pending',
102+
$pods[Metrics::POD_STATE_FAILED] . ' Failed',
103+
$pods[Metrics::POD_STATE_SUCCEEDED] . ' Succeeded'
104+
]
105+
),
106+
'#007bff, #ffc107, #dc3545, #28a745'
107+
)
108+
);
109+
110+
$current = $metrics->getClusterMetrics(
111+
(new DateTime())->sub(new DateInterval('PT2M')),
112+
Metrics::CLUSTER_CPU_USAGE,
113+
Metrics::CLUSTER_MEMORY_USAGE
114+
);
115+
116+
$this->addContent(
117+
new DoughnutChartStates(
118+
'chart-small',
119+
$current[Metrics::CLUSTER_CPU_USAGE][array_key_last($current[Metrics::CLUSTER_CPU_USAGE])],
120+
'CPU Usage',
121+
'#28a745, #ffc107, #dc3545'
122+
)
123+
);
124+
125+
$this->addContent(
126+
new DoughnutChartStates(
127+
'chart-small',
128+
$current[Metrics::CLUSTER_MEMORY_USAGE][array_key_last($current[Metrics::CLUSTER_MEMORY_USAGE])],
129+
'Memory Usage',
130+
'#28a745, #ffc107, #dc3545'
131+
)
132+
);
133+
}
134+
135+
public function nodeAction(): void
136+
{
137+
$this->addTitleTab($this->translate('Node Metrics'));
138+
139+
$this->addContent(
140+
new HtmlElement(
141+
'a',
142+
new Attributes(['href' => '/icingaweb2/kubernetes/charts']),
143+
new Text('Back to Charts')
144+
)
145+
);
146+
147+
$metrics = new Metrics(Database::connection());
148+
149+
$nodeNetworkMetrics = $metrics->getNodesMetrics(
150+
(new DateTime())->sub(new DateInterval('PT1H')),
151+
Metrics::NODE_NETWORK_RECEIVED_BYTES,
152+
Metrics::NODE_NETWORK_TRANSMITTED_BYTES
153+
);
154+
155+
156+
foreach ($nodeNetworkMetrics as $node) {
157+
$this->addContent(
158+
new LineChart(
159+
'chart-medium',
160+
implode(', ', $node[Metrics::NODE_NETWORK_RECEIVED_BYTES])
161+
. '; '
162+
. implode(', ', $node[Metrics::NODE_NETWORK_TRANSMITTED_BYTES]),
163+
implode(', ', array_keys($node[Metrics::NODE_NETWORK_RECEIVED_BYTES])),
164+
'Received Bytes; Transmitted Bytes',
165+
'#593684; #a3367f'
166+
)
167+
);
168+
}
169+
}
170+
171+
public function podAction(): void
172+
{
173+
$this->addTitleTab($this->translate('Pod Metrics'));
174+
175+
$this->addContent(
176+
new HtmlElement(
177+
'a',
178+
new Attributes(['href' => '/icingaweb2/kubernetes/charts']),
179+
new Text('Back to Charts')
180+
)
181+
);
182+
183+
$metrics = new Metrics(Database::connection());
184+
185+
$podMetricsCurrent = $metrics->getPodsMetricsCurrent(
186+
Metrics::POD_CPU_REQUEST,
187+
Metrics::POD_CPU_LIMIT,
188+
Metrics::POD_CPU_USAGE_CORES,
189+
Metrics::POD_MEMORY_REQUEST,
190+
Metrics::POD_MEMORY_LIMIT,
191+
Metrics::POD_MEMORY_USAGE_BYTES
192+
);
193+
194+
$podMetricsPeriod = $metrics->getPodsMetrics(
195+
(new DateTime())->sub(new DateInterval('PT1H')),
196+
Metrics::POD_CPU_USAGE,
197+
Metrics::POD_MEMORY_USAGE
198+
);
199+
200+
$podMetrics = Metrics::mergeMetrics($podMetricsCurrent, $podMetricsPeriod);
201+
202+
$table = new HtmlElement('table', new Attributes(['class' => 'condition-table']));
203+
$table->addHtml(
204+
new HtmlElement(
205+
'thead',
206+
null,
207+
new HtmlElement(
208+
'tr',
209+
null,
210+
new HtmlElement('th', null, new Text('Pod Name')),
211+
new HtmlElement('th', null, new Text('Cpu')),
212+
new HtmlElement('th', null, new Text('Memory')),
213+
new HtmlElement('th', null, new Text('Cpu')),
214+
new HtmlElement('th', null, new Text('Memory'))
215+
)
216+
)
217+
);
218+
219+
$tbody = new HtmlElement('tbody');
220+
221+
foreach ($podMetrics as $pod) {
222+
$tr = new HtmlElement('tr');
223+
$tr->addHtml(new HtmlElement('td', null, new Text($pod['name'])));
224+
$td = new HtmlElement('td', null);
225+
226+
if (isset($pod[Metrics::POD_CPU_LIMIT]) && $pod[Metrics::POD_CPU_REQUEST] < $pod[Metrics::POD_CPU_LIMIT]) {
227+
$td->addHtml(
228+
new DoughnutChartRequestLimit(
229+
'chart-mini',
230+
$pod[Metrics::POD_CPU_REQUEST],
231+
$pod[Metrics::POD_CPU_LIMIT],
232+
$pod[Metrics::POD_CPU_USAGE_CORES]
233+
)
234+
);
235+
}
236+
237+
$tr->addHtml($td);
238+
$td = new HtmlElement('td', null);
239+
240+
if (
241+
isset($pod[Metrics::POD_MEMORY_LIMIT])
242+
&& $pod[Metrics::POD_MEMORY_REQUEST] < $pod[Metrics::POD_MEMORY_LIMIT]
243+
) {
244+
$td->addHtml(
245+
new DoughnutChartRequestLimit(
246+
'chart-mini',
247+
$pod[Metrics::POD_MEMORY_REQUEST],
248+
$pod[Metrics::POD_MEMORY_LIMIT],
249+
$pod[Metrics::POD_MEMORY_USAGE_BYTES]
250+
)
251+
);
252+
}
253+
254+
$tr->addHtml($td);
255+
$td = new HtmlElement('td', null);
256+
257+
if (isset($pod[Metrics::POD_CPU_USAGE])) {
258+
$td->addHtml(
259+
new LineChartMinified(
260+
'chart-mini',
261+
implode(', ', $pod[Metrics::POD_CPU_USAGE]),
262+
implode(', ', array_keys($pod[Metrics::POD_CPU_USAGE])),
263+
'#00a8ff'
264+
)
265+
);
266+
}
267+
268+
$tr->addHtml($td);
269+
$td = new HtmlElement('td', null);
270+
271+
if (isset($pod[Metrics::POD_MEMORY_USAGE])) {
272+
$td->addHtml(
273+
new LineChartMinified(
274+
'chart-mini',
275+
implode(', ', $pod[Metrics::POD_MEMORY_USAGE]),
276+
implode(', ', array_keys($pod[Metrics::POD_MEMORY_USAGE])),
277+
'#ffa800'
278+
)
279+
);
280+
}
281+
$tr->addHtml($td);
282+
$tbody->addHtml($tr);
283+
}
284+
285+
$table->addHtml($tbody);
286+
$this->addContent($table);
287+
}
288+
289+
public function containerAction(): void
290+
{
291+
$this->addTitleTab($this->translate('Container Metrics'));
292+
293+
$this->addContent(
294+
new HtmlElement(
295+
'a',
296+
new Attributes(['href' => '/icingaweb2/kubernetes/charts']),
297+
new Text('Back to Charts')
298+
)
299+
);
300+
}
301+
}

configuration.php

+11
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,15 @@
159159
]
160160
);
161161

162+
$section->add(
163+
N_('Charts'),
164+
[
165+
'description' => $this->translate('Charts'),
166+
'url' => 'kubernetes/charts',
167+
'priority' => $priority++
168+
]
169+
);
170+
162171
$this->provideConfigTab(
163172
'database',
164173
[
@@ -167,3 +176,5 @@
167176
'url' => 'config/database'
168177
]
169178
);
179+
180+
$this->provideJsFile('vendor/chart.umd.js');

0 commit comments

Comments
 (0)