mirror of https://github.com/portainer/portainer
				
				
				
			Feat 4612 real time metrics for kube nodes (#4708)
* feat(k8s/node): display realtime node metrics GH#4612 * feat(k8s): show observation timestamp instead of real timestamp GH#4612 Co-authored-by: Simon Meng <simon.meng@portainer.io>pull/5047/head^2
							parent
							
								
									45ceece1a9
								
							
						
					
					
						commit
						dc180d85c5
					
				| 
						 | 
					@ -30,3 +30,5 @@ angular
 | 
				
			||||||
  .constant('PREDEFINED_NETWORKS', ['host', 'bridge', 'none'])
 | 
					  .constant('PREDEFINED_NETWORKS', ['host', 'bridge', 'none'])
 | 
				
			||||||
  .constant('KUBERNETES_DEFAULT_NAMESPACE', 'default')
 | 
					  .constant('KUBERNETES_DEFAULT_NAMESPACE', 'default')
 | 
				
			||||||
  .constant('KUBERNETES_SYSTEM_NAMESPACES', ['kube-system', 'kube-public', 'kube-node-lease', 'portainer']);
 | 
					  .constant('KUBERNETES_SYSTEM_NAMESPACES', ['kube-system', 'kube-public', 'kube-node-lease', 'portainer']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const PORTAINER_FADEOUT = 1500;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -182,6 +182,16 @@ angular.module('portainer.kubernetes', ['portainer.app']).config([
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const nodeStats = {
 | 
				
			||||||
 | 
					      name: 'kubernetes.cluster.node.stats',
 | 
				
			||||||
 | 
					      url: '/stats',
 | 
				
			||||||
 | 
					      views: {
 | 
				
			||||||
 | 
					        'content@': {
 | 
				
			||||||
 | 
					          component: 'kubernetesNodeStatsView',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const dashboard = {
 | 
					    const dashboard = {
 | 
				
			||||||
      name: 'kubernetes.dashboard',
 | 
					      name: 'kubernetes.dashboard',
 | 
				
			||||||
      url: '/dashboard',
 | 
					      url: '/dashboard',
 | 
				
			||||||
| 
						 | 
					@ -280,6 +290,7 @@ angular.module('portainer.kubernetes', ['portainer.app']).config([
 | 
				
			||||||
    $stateRegistryProvider.register(dashboard);
 | 
					    $stateRegistryProvider.register(dashboard);
 | 
				
			||||||
    $stateRegistryProvider.register(deploy);
 | 
					    $stateRegistryProvider.register(deploy);
 | 
				
			||||||
    $stateRegistryProvider.register(node);
 | 
					    $stateRegistryProvider.register(node);
 | 
				
			||||||
 | 
					    $stateRegistryProvider.register(nodeStats);
 | 
				
			||||||
    $stateRegistryProvider.register(resourcePools);
 | 
					    $stateRegistryProvider.register(resourcePools);
 | 
				
			||||||
    $stateRegistryProvider.register(resourcePoolCreation);
 | 
					    $stateRegistryProvider.register(resourcePoolCreation);
 | 
				
			||||||
    $stateRegistryProvider.register(resourcePool);
 | 
					    $stateRegistryProvider.register(resourcePool);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,6 +107,9 @@
 | 
				
			||||||
                  <i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAddress' && $ctrl.state.reverseOrder"></i>
 | 
					                  <i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAddress' && $ctrl.state.reverseOrder"></i>
 | 
				
			||||||
                </a>
 | 
					                </a>
 | 
				
			||||||
              </th>
 | 
					              </th>
 | 
				
			||||||
 | 
					              <th ng-if="$ctrl.useServerMetrics">
 | 
				
			||||||
 | 
					                Actions
 | 
				
			||||||
 | 
					              </th>
 | 
				
			||||||
            </tr>
 | 
					            </tr>
 | 
				
			||||||
          </thead>
 | 
					          </thead>
 | 
				
			||||||
          <tbody>
 | 
					          <tbody>
 | 
				
			||||||
| 
						 | 
					@ -128,6 +131,9 @@
 | 
				
			||||||
              <td>{{ item.Memory | humansize }}</td>
 | 
					              <td>{{ item.Memory | humansize }}</td>
 | 
				
			||||||
              <td>{{ item.Version }}</td>
 | 
					              <td>{{ item.Version }}</td>
 | 
				
			||||||
              <td>{{ item.IPAddress }}</td>
 | 
					              <td>{{ item.IPAddress }}</td>
 | 
				
			||||||
 | 
					              <td ng-if="$ctrl.useServerMetrics">
 | 
				
			||||||
 | 
					                <a ui-sref="kubernetes.cluster.node.stats({ name: item.Name })" style="cursor: pointer;"> <i class="fa fa-chart-area" aria-hidden="true"></i> Stats </a>
 | 
				
			||||||
 | 
					              </td>
 | 
				
			||||||
            </tr>
 | 
					            </tr>
 | 
				
			||||||
            <tr ng-if="!$ctrl.dataset">
 | 
					            <tr ng-if="!$ctrl.dataset">
 | 
				
			||||||
              <td colspan="7" class="text-center text-muted">Loading...</td>
 | 
					              <td colspan="7" class="text-center text-muted">Loading...</td>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,5 +9,6 @@ angular.module('portainer.kubernetes').component('kubernetesNodesDatatable', {
 | 
				
			||||||
    orderBy: '@',
 | 
					    orderBy: '@',
 | 
				
			||||||
    refreshCallback: '<',
 | 
					    refreshCallback: '<',
 | 
				
			||||||
    isAdmin: '<',
 | 
					    isAdmin: '<',
 | 
				
			||||||
 | 
					    useServerMetrics: '<',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ class KubernetesMetricsService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.capabilitiesAsync = this.capabilitiesAsync.bind(this);
 | 
					    this.capabilitiesAsync = this.capabilitiesAsync.bind(this);
 | 
				
			||||||
    this.getPodAsync = this.getPodAsync.bind(this);
 | 
					    this.getPodAsync = this.getPodAsync.bind(this);
 | 
				
			||||||
 | 
					    this.getNodeAsync = this.getNodeAsync.bind(this);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
| 
						 | 
					@ -27,6 +28,26 @@ class KubernetesMetricsService {
 | 
				
			||||||
    return this.$async(this.capabilitiesAsync, endpointID);
 | 
					    return this.$async(this.capabilitiesAsync, endpointID);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Stats of Node
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param {string} nodeName
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  async getNodeAsync(nodeName) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const params = new KubernetesCommonParams();
 | 
				
			||||||
 | 
					      params.id = nodeName;
 | 
				
			||||||
 | 
					      const data = await this.KubernetesMetrics().getNode(params).$promise;
 | 
				
			||||||
 | 
					      return data;
 | 
				
			||||||
 | 
					    } catch (err) {
 | 
				
			||||||
 | 
					      throw new PortainerError('Unable to retrieve node stats', err);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getNode(nodeName) {
 | 
				
			||||||
 | 
					    return this.$async(this.getNodeAsync, nodeName);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Stats
 | 
					   * Stats
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,10 @@ angular.module('portainer.kubernetes').factory('KubernetesMetrics', [
 | 
				
			||||||
            method: 'GET',
 | 
					            method: 'GET',
 | 
				
			||||||
            url: podUrl,
 | 
					            url: podUrl,
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
 | 
					          getNode: {
 | 
				
			||||||
 | 
					            method: 'GET',
 | 
				
			||||||
 | 
					            url: `${url}/nodes/:id`,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -89,6 +89,7 @@
 | 
				
			||||||
        order-by="Name"
 | 
					        order-by="Name"
 | 
				
			||||||
        refresh-callback="ctrl.getNodes"
 | 
					        refresh-callback="ctrl.getNodes"
 | 
				
			||||||
        is-admin="ctrl.isAdmin"
 | 
					        is-admin="ctrl.isAdmin"
 | 
				
			||||||
 | 
					        use-server-metrics="ctrl.state.useServerMetrics"
 | 
				
			||||||
      ></kubernetes-nodes-datatable>
 | 
					      ></kubernetes-nodes-datatable>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,8 @@ class KubernetesClusterController {
 | 
				
			||||||
    KubernetesNodeService,
 | 
					    KubernetesNodeService,
 | 
				
			||||||
    KubernetesApplicationService,
 | 
					    KubernetesApplicationService,
 | 
				
			||||||
    KubernetesComponentStatusService,
 | 
					    KubernetesComponentStatusService,
 | 
				
			||||||
    KubernetesEndpointService
 | 
					    KubernetesEndpointService,
 | 
				
			||||||
 | 
					    EndpointProvider
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.$async = $async;
 | 
					    this.$async = $async;
 | 
				
			||||||
    this.$state = $state;
 | 
					    this.$state = $state;
 | 
				
			||||||
| 
						 | 
					@ -26,6 +27,7 @@ class KubernetesClusterController {
 | 
				
			||||||
    this.KubernetesApplicationService = KubernetesApplicationService;
 | 
					    this.KubernetesApplicationService = KubernetesApplicationService;
 | 
				
			||||||
    this.KubernetesComponentStatusService = KubernetesComponentStatusService;
 | 
					    this.KubernetesComponentStatusService = KubernetesComponentStatusService;
 | 
				
			||||||
    this.KubernetesEndpointService = KubernetesEndpointService;
 | 
					    this.KubernetesEndpointService = KubernetesEndpointService;
 | 
				
			||||||
 | 
					    this.EndpointProvider = EndpointProvider;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.onInit = this.onInit.bind(this);
 | 
					    this.onInit = this.onInit.bind(this);
 | 
				
			||||||
    this.getNodes = this.getNodes.bind(this);
 | 
					    this.getNodes = this.getNodes.bind(this);
 | 
				
			||||||
| 
						 | 
					@ -132,6 +134,7 @@ class KubernetesClusterController {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.state.viewReady = true;
 | 
					    this.state.viewReady = true;
 | 
				
			||||||
 | 
					    this.state.useServerMetrics = this.EndpointProvider.currentEndpoint().Kubernetes.Configuration.UseServerMetrics;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  $onInit() {
 | 
					  $onInit() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,71 @@
 | 
				
			||||||
 | 
					<kubernetes-view-header title="Node stats" state="kubernetes.cluster.node.stats" view-ready="ctrl.state.viewReady">
 | 
				
			||||||
 | 
					  <a ui-sref="kubernetes.cluster">Cluster</a> > <a ui-sref="kubernetes.cluster.node({name: ctrl.state.transition.nodeName})"> {{ ctrl.state.transition.nodeName }} </a> >
 | 
				
			||||||
 | 
					  {{ ctrl.state.transition.nodeName }}
 | 
				
			||||||
 | 
					</kubernetes-view-header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<kubernetes-view-loading view-ready="ctrl.state.viewReady"></kubernetes-view-loading>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div ng-if="ctrl.state.viewReady">
 | 
				
			||||||
 | 
					  <information-panel ng-if="!ctrl.state.getMetrics" title-text="Unable to retrieve node metrics">
 | 
				
			||||||
 | 
					    <span class="small text-muted">
 | 
				
			||||||
 | 
					      <i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
 | 
				
			||||||
 | 
					      Portainer was unable to retrieve any metrics associated to that node. Please contact your administrator to ensure that the Kubernetes metrics feature is properly configured.
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					  </information-panel>
 | 
				
			||||||
 | 
					  <div class="row" ng-if="ctrl.state.getMetrics">
 | 
				
			||||||
 | 
					    <div class="col-md-12">
 | 
				
			||||||
 | 
					      <rd-widget>
 | 
				
			||||||
 | 
					        <rd-widget-header icon="fa-info-circle" title-text="About statistics"> </rd-widget-header>
 | 
				
			||||||
 | 
					        <rd-widget-body>
 | 
				
			||||||
 | 
					          <form class="form-horizontal">
 | 
				
			||||||
 | 
					            <div class="form-group">
 | 
				
			||||||
 | 
					              <div class="col-sm-12">
 | 
				
			||||||
 | 
					                <span class="small text-muted">
 | 
				
			||||||
 | 
					                  This view displays real-time statistics about the node <b>{{ ctrl.state.transition.nodeName }}</b
 | 
				
			||||||
 | 
					                  >.
 | 
				
			||||||
 | 
					                </span>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="form-group">
 | 
				
			||||||
 | 
					              <label for="refreshRate" class="col-sm-3 col-md-2 col-lg-2 margin-sm-top control-label text-left">
 | 
				
			||||||
 | 
					                Refresh rate
 | 
				
			||||||
 | 
					              </label>
 | 
				
			||||||
 | 
					              <div class="col-sm-3 col-md-2">
 | 
				
			||||||
 | 
					                <select id="refreshRate" ng-model="ctrl.state.refreshRate" ng-change="ctrl.changeUpdateRepeater()" class="form-control">
 | 
				
			||||||
 | 
					                  <option value="30">30s</option>
 | 
				
			||||||
 | 
					                  <option value="60">60s</option>
 | 
				
			||||||
 | 
					                </select>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					              <span>
 | 
				
			||||||
 | 
					                <i id="refreshRateChange" class="fa fa-check green-icon" aria-hidden="true" style="margin-top: 7px; display: none;"></i>
 | 
				
			||||||
 | 
					              </span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </form>
 | 
				
			||||||
 | 
					        </rd-widget-body>
 | 
				
			||||||
 | 
					      </rd-widget>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div class="row" ng-show="ctrl.state.getMetrics">
 | 
				
			||||||
 | 
					    <div class="col-lg-6 col-md-12 col-sm-12">
 | 
				
			||||||
 | 
					      <rd-widget>
 | 
				
			||||||
 | 
					        <rd-widget-header icon="fa-chart-area" title-text="Memory usage"></rd-widget-header>
 | 
				
			||||||
 | 
					        <rd-widget-body>
 | 
				
			||||||
 | 
					          <div class="chart-node" style="position: relative;">
 | 
				
			||||||
 | 
					            <canvas id="memoryChart" width="770" height="300"></canvas>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </rd-widget-body>
 | 
				
			||||||
 | 
					      </rd-widget>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="col-lg-6 col-md-12 col-sm-12">
 | 
				
			||||||
 | 
					      <rd-widget>
 | 
				
			||||||
 | 
					        <rd-widget-header icon="fa-chart-area" title-text="CPU usage"></rd-widget-header>
 | 
				
			||||||
 | 
					        <rd-widget-body>
 | 
				
			||||||
 | 
					          <div class="chart-node" style="position: relative;">
 | 
				
			||||||
 | 
					            <canvas id="cpuChart" width="770" height="300"></canvas>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </rd-widget-body>
 | 
				
			||||||
 | 
					      </rd-widget>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					angular.module('portainer.kubernetes').component('kubernetesNodeStatsView', {
 | 
				
			||||||
 | 
					  templateUrl: './stats.html',
 | 
				
			||||||
 | 
					  controller: 'KubernetesNodeStatsController',
 | 
				
			||||||
 | 
					  controllerAs: 'ctrl',
 | 
				
			||||||
 | 
					  bindings: {
 | 
				
			||||||
 | 
					    $transition$: '<',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,144 @@
 | 
				
			||||||
 | 
					import angular from 'angular';
 | 
				
			||||||
 | 
					import moment from 'moment';
 | 
				
			||||||
 | 
					import filesizeParser from 'filesize-parser';
 | 
				
			||||||
 | 
					import KubernetesResourceReservationHelper from 'Kubernetes/helpers/resourceReservationHelper';
 | 
				
			||||||
 | 
					import { PORTAINER_FADEOUT } from '@/constants';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class KubernetesNodeStatsController {
 | 
				
			||||||
 | 
					  /* @ngInject */
 | 
				
			||||||
 | 
					  constructor($async, $state, $interval, $document, Notifications, KubernetesNodeService, KubernetesMetricsService, ChartService) {
 | 
				
			||||||
 | 
					    this.$async = $async;
 | 
				
			||||||
 | 
					    this.$state = $state;
 | 
				
			||||||
 | 
					    this.$interval = $interval;
 | 
				
			||||||
 | 
					    this.$document = $document;
 | 
				
			||||||
 | 
					    this.Notifications = Notifications;
 | 
				
			||||||
 | 
					    this.KubernetesNodeService = KubernetesNodeService;
 | 
				
			||||||
 | 
					    this.KubernetesMetricsService = KubernetesMetricsService;
 | 
				
			||||||
 | 
					    this.ChartService = ChartService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.onInit = this.onInit.bind(this);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  changeUpdateRepeater() {
 | 
				
			||||||
 | 
					    var cpuChart = this.cpuChart;
 | 
				
			||||||
 | 
					    var memoryChart = this.memoryChart;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.stopRepeater();
 | 
				
			||||||
 | 
					    this.setUpdateRepeater(cpuChart, memoryChart);
 | 
				
			||||||
 | 
					    $('#refreshRateChange').show();
 | 
				
			||||||
 | 
					    $('#refreshRateChange').fadeOut(PORTAINER_FADEOUT);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  updateCPUChart() {
 | 
				
			||||||
 | 
					    const label = moment(this.stats.read).format('HH:mm:ss');
 | 
				
			||||||
 | 
					    this.ChartService.UpdateCPUChart(label, this.stats.CPUUsage, this.cpuChart);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  updateMemoryChart() {
 | 
				
			||||||
 | 
					    const label = moment(this.stats.read).format('HH:mm:ss');
 | 
				
			||||||
 | 
					    this.ChartService.UpdateMemoryChart(label, this.stats.MemoryUsage, 0, this.memoryChart);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  stopRepeater() {
 | 
				
			||||||
 | 
					    var repeater = this.repeater;
 | 
				
			||||||
 | 
					    if (angular.isDefined(repeater)) {
 | 
				
			||||||
 | 
					      this.$interval.cancel(repeater);
 | 
				
			||||||
 | 
					      this.repeater = undefined;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setUpdateRepeater() {
 | 
				
			||||||
 | 
					    const refreshRate = this.state.refreshRate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.repeater = this.$interval(async () => {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        await this.getStats();
 | 
				
			||||||
 | 
					        this.updateCPUChart();
 | 
				
			||||||
 | 
					        this.updateMemoryChart();
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        this.stopRepeater();
 | 
				
			||||||
 | 
					        this.Notifications.error('Failure', error);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, refreshRate * 1000);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  initCharts() {
 | 
				
			||||||
 | 
					    const cpuChartCtx = $('#cpuChart');
 | 
				
			||||||
 | 
					    const cpuChart = this.ChartService.CreateCPUChart(cpuChartCtx);
 | 
				
			||||||
 | 
					    this.cpuChart = cpuChart;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const memoryChartCtx = $('#memoryChart');
 | 
				
			||||||
 | 
					    const memoryChart = this.ChartService.CreateMemoryChart(memoryChartCtx);
 | 
				
			||||||
 | 
					    this.memoryChart = memoryChart;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.updateCPUChart();
 | 
				
			||||||
 | 
					    this.updateMemoryChart();
 | 
				
			||||||
 | 
					    this.setUpdateRepeater();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getStats() {
 | 
				
			||||||
 | 
					    return this.$async(async () => {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        const stats = await this.KubernetesMetricsService.getNode(this.state.transition.nodeName);
 | 
				
			||||||
 | 
					        if (stats) {
 | 
				
			||||||
 | 
					          const memory = filesizeParser(stats.usage.memory);
 | 
				
			||||||
 | 
					          const cpu = KubernetesResourceReservationHelper.parseCPU(stats.usage.cpu);
 | 
				
			||||||
 | 
					          this.stats = {
 | 
				
			||||||
 | 
					            read: stats.creationTimestamp,
 | 
				
			||||||
 | 
					            MemoryUsage: memory,
 | 
				
			||||||
 | 
					            CPUUsage: (cpu / this.nodeCPU) * 100,
 | 
				
			||||||
 | 
					          };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } catch (err) {
 | 
				
			||||||
 | 
					        this.Notifications.error('Failure', err, 'Unable to retrieve node stats');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  $onDestroy() {
 | 
				
			||||||
 | 
					    this.stopRepeater();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async onInit() {
 | 
				
			||||||
 | 
					    this.state = {
 | 
				
			||||||
 | 
					      autoRefresh: false,
 | 
				
			||||||
 | 
					      refreshRate: '30',
 | 
				
			||||||
 | 
					      viewReady: false,
 | 
				
			||||||
 | 
					      transition: {
 | 
				
			||||||
 | 
					        nodeName: this.$transition$.params().name,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      getMetrics: true,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const nodeMetrics = await this.KubernetesMetricsService.getNode(this.state.transition.nodeName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (nodeMetrics) {
 | 
				
			||||||
 | 
					        const node = await this.KubernetesNodeService.get(this.state.transition.nodeName);
 | 
				
			||||||
 | 
					        this.nodeCPU = node.CPU || 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await this.getStats();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.state.getMetrics) {
 | 
				
			||||||
 | 
					          this.$document.ready(() => {
 | 
				
			||||||
 | 
					            this.initCharts();
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.state.getMetrics = false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (err) {
 | 
				
			||||||
 | 
					      this.state.getMetrics = false;
 | 
				
			||||||
 | 
					      this.Notifications.error('Failure', err, 'Unable to retrieve node stats');
 | 
				
			||||||
 | 
					    } finally {
 | 
				
			||||||
 | 
					      this.state.viewReady = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  $onInit() {
 | 
				
			||||||
 | 
					    return this.$async(this.onInit);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default KubernetesNodeStatsController;
 | 
				
			||||||
 | 
					angular.module('portainer.kubernetes').controller('KubernetesNodeStatsController', KubernetesNodeStatsController);
 | 
				
			||||||
		Loading…
	
		Reference in New Issue