From 6e9f47272350962434a7d3a579570fe1e1750681 Mon Sep 17 00:00:00 2001 From: zees-dev <63374656+zees-dev@users.noreply.github.com> Date: Mon, 14 Jun 2021 15:57:00 +1200 Subject: [PATCH] feat(container-stats): introduce container block I/O stats (#5017) * feat(container-stats):introduce container block io stats * Change charts to 2x2 view * fix(container-stats): handle missing io stats by detecting stats based on op codes Co-authored-by: DarkAEther <30438425+DarkAEther@users.noreply.github.com> --- app/docker/models/container.js | 14 ++++++ .../stats/containerStatsController.js | 28 +++++++++--- .../containers/stats/containerstats.html | 26 +++++++++-- app/portainer/services/chartService.js | 45 +++++++++++++++++++ 4 files changed, 105 insertions(+), 8 deletions(-) diff --git a/app/docker/models/container.js b/app/docker/models/container.js index 58cc51a20..dafcdb63c 100644 --- a/app/docker/models/container.js +++ b/app/docker/models/container.js @@ -91,6 +91,20 @@ export function ContainerStatsViewModel(data) { this.CPUCores = data.cpu_stats.cpu_usage.percpu_usage.length; } this.Networks = _.values(data.networks); + if (data.blkio_stats !== undefined) { + //TODO: take care of multiple block devices + var readData = data.blkio_stats.io_service_bytes_recursive.find((d) => d.op === 'Read'); + if (readData !== undefined) { + this.BytesRead = readData.value; + } + var writeData = data.blkio_stats.io_service_bytes_recursive.find((d) => d.op === 'Write'); + if (writeData !== undefined) { + this.BytesWrite = writeData.value; + } + } else { + //no IO related data is available + this.noIOdata = true; + } } export function ContainerDetailsViewModel(data) { diff --git a/app/docker/views/containers/stats/containerStatsController.js b/app/docker/views/containers/stats/containerStatsController.js index 9fef44e41..48803264f 100644 --- a/app/docker/views/containers/stats/containerStatsController.js +++ b/app/docker/views/containers/stats/containerStatsController.js @@ -14,6 +14,7 @@ angular.module('portainer.docker').controller('ContainerStatsController', [ $scope.state = { refreshRate: '5', networkStatsUnavailable: false, + ioStatsUnavailable: false, }; $scope.$on('$destroy', function () { @@ -44,6 +45,13 @@ angular.module('portainer.docker').controller('ContainerStatsController', [ ChartService.UpdateMemoryChart(label, stats.MemoryUsage, stats.MemoryCache, chart); } + function updateIOChart(stats, chart) { + var label = moment(stats.read).format('HH:mm:ss'); + if (stats.noIOData !== true) { + ChartService.UpdateIOChart(label, stats.BytesRead, stats.BytesWrite, chart); + } + } + function updateCPUChart(stats, chart) { var label = moment(stats.read).format('HH:mm:ss'); var value = stats.isWindows ? calculateCPUPercentWindows(stats) : calculateCPUPercentUnix(stats); @@ -77,14 +85,15 @@ angular.module('portainer.docker').controller('ContainerStatsController', [ var networkChart = $scope.networkChart; var cpuChart = $scope.cpuChart; var memoryChart = $scope.memoryChart; + var ioChart = $scope.ioChart; stopRepeater(); - setUpdateRepeater(networkChart, cpuChart, memoryChart); + setUpdateRepeater(networkChart, cpuChart, memoryChart, ioChart); $('#refreshRateChange').show(); $('#refreshRateChange').fadeOut(1500); }; - function startChartUpdate(networkChart, cpuChart, memoryChart) { + function startChartUpdate(networkChart, cpuChart, memoryChart, ioChart) { $q.all({ stats: ContainerService.containerStats($transition$.params().id), top: ContainerService.containerTop($transition$.params().id), @@ -95,10 +104,14 @@ angular.module('portainer.docker').controller('ContainerStatsController', [ if (stats.Networks.length === 0) { $scope.state.networkStatsUnavailable = true; } + if (stats.noIOData === true) { + $scope.state.ioStatsUnavailable = true; + } updateNetworkChart(stats, networkChart); updateMemoryChart(stats, memoryChart); updateCPUChart(stats, cpuChart); - setUpdateRepeater(networkChart, cpuChart, memoryChart); + updateIOChart(stats, ioChart); + setUpdateRepeater(networkChart, cpuChart, memoryChart, ioChart); }) .catch(function error(err) { stopRepeater(); @@ -106,7 +119,7 @@ angular.module('portainer.docker').controller('ContainerStatsController', [ }); } - function setUpdateRepeater(networkChart, cpuChart, memoryChart) { + function setUpdateRepeater(networkChart, cpuChart, memoryChart, ioChart) { var refreshRate = $scope.state.refreshRate; $scope.repeater = $interval(function () { $q.all({ @@ -119,6 +132,7 @@ angular.module('portainer.docker').controller('ContainerStatsController', [ updateNetworkChart(stats, networkChart); updateMemoryChart(stats, memoryChart); updateCPUChart(stats, cpuChart); + updateIOChart(stats, ioChart); }) .catch(function error(err) { stopRepeater(); @@ -140,7 +154,11 @@ angular.module('portainer.docker').controller('ContainerStatsController', [ var memoryChart = ChartService.CreateMemoryChart(memoryChartCtx); $scope.memoryChart = memoryChart; - startChartUpdate(networkChart, cpuChart, memoryChart); + var ioChartCtx = $('#ioChart'); + var ioChart = ChartService.CreateIOChart(ioChartCtx); + $scope.ioChart = ioChart; + + startChartUpdate(networkChart, cpuChart, memoryChart, ioChart); } function initView() { diff --git a/app/docker/views/containers/stats/containerstats.html b/app/docker/views/containers/stats/containerstats.html index 0a9d019c8..c6fc413f4 100644 --- a/app/docker/views/containers/stats/containerstats.html +++ b/app/docker/views/containers/stats/containerstats.html @@ -42,6 +42,11 @@ Network stats are unavailable for this container. +
+
+ I/O stats are unavailable for this container. +
+
@@ -49,7 +54,7 @@
-
+
@@ -59,7 +64,8 @@
-
+ +
@@ -69,7 +75,8 @@
-
+ +
@@ -80,6 +87,19 @@
+
+ + + +
+ +
+
+
+
+
+ +