Skip to content

Commit 9c794ed

Browse files
committed
Add cluster quota details to the project settings page
1 parent ccd995f commit 9c794ed

File tree

6 files changed

+276
-88
lines changed

6 files changed

+276
-88
lines changed

app/scripts/controllers/settings.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@
1111
angular.module('openshiftConsole')
1212
.controller('SettingsController', function ($routeParams, $scope, DataService, ProjectsService, AlertMessageService, $filter, $location, LabelFilter, $timeout, Logger, annotationFilter, annotationNameFilter) {
1313
$scope.projectName = $routeParams.project;
14-
$scope.quotas = {};
1514
$scope.limitRanges = {};
1615
$scope.limitsByType = {};
1716
$scope.labelSuggestions = {};
1817
$scope.alerts = $scope.alerts || {};
19-
$scope.emptyMessageQuotas = "Loading...";
20-
$scope.quotaHelp = "Limits resource usage within the project.";
18+
$scope.quotaHelp = "Limits resource usage within this project.";
2119
$scope.emptyMessageLimitRanges = "Loading...";
2220
$scope.limitRangeHelp = "Defines minimum and maximum constraints for runtime resources such as memory and CPU.";
2321
$scope.renderOptions = $scope.renderOptions || {};
@@ -83,10 +81,21 @@ angular.module('openshiftConsole')
8381

8482
DataService.list("resourcequotas", context, function(quotas) {
8583
$scope.quotas = quotas.by("metadata.name");
86-
$scope.emptyMessageQuotas = "There are no resource quotas set on this project.";
8784
Logger.log("quotas", $scope.quotas);
8885
});
8986

87+
DataService.list("appliedclusterresourcequotas", context, function(quotas) {
88+
$scope.clusterQuotas = quotas.by("metadata.name");
89+
$scope.namespaceUsageByClusterQuota = {};
90+
_.each($scope.clusterQuotas, function(quota, quotaName) {
91+
if (quota.status) {
92+
var namespaceUsage = _.find(quota.status.namespaces, { namespace: $routeParams.project });
93+
$scope.namespaceUsageByClusterQuota[quotaName] = namespaceUsage.status;
94+
}
95+
});
96+
Logger.log("cluster quotas", $scope.clusterQuotas);
97+
});
98+
9099
DataService.list("limitranges", context, function(limitRanges) {
91100
$scope.limitRanges = limitRanges.by("metadata.name");
92101
$scope.emptyMessageLimitRanges = "There are no limit ranges set on this project.";

app/scripts/directives/quotaUsageChart.js

+23-18
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ angular.module('openshiftConsole')
66
restrict: 'E',
77
scope: {
88
used: '=',
9+
crossProjectUsed: '=?',
910
total: '=',
1011
// 'cpu' or 'memory'
1112
type: '@',
1213
// Defaults to 'bottom'.
1314
// http://c3js.org/reference.html#legend-position
14-
legendPosition: '@?'
15+
height: '=?',
16+
width: '=?'
1517
},
1618
// Replace the element so it can be centered using class="center-block".
1719
replace: true,
@@ -28,14 +30,8 @@ angular.module('openshiftConsole')
2830
replaceText(amountAndUnit($scope.total, $scope.type, true));
2931
}
3032

31-
// Adjust size based on legend position.
32-
if ($scope.legendPosition === 'right') {
33-
$scope.height = 175;
34-
$scope.width = 250;
35-
} else {
36-
$scope.height = 200;
37-
$scope.width = 175;
38-
}
33+
$scope.height = $scope.height || 200;
34+
$scope.width = $scope.width || 175;
3935

4036
var percentage = function(value) {
4137
if (!value) {
@@ -131,30 +127,39 @@ angular.module('openshiftConsole')
131127

132128
var chart;
133129
var updateChart = function() {
130+
var hasCrossProject = $scope.crossProjectUsed !== undefined;
134131
var used = usageValue($scope.used) || 0,
135-
available = Math.max(usageValue($scope.total) - used, 0),
132+
crossProjectUsed = Math.max((usageValue($scope.crossProjectUsed) || 0) - used, 0),
133+
available = Math.max(usageValue($scope.total) - (crossProjectUsed + used), 0),
136134
data = {
137135
columns: [
138-
['Used', used],
139-
['Available', available]
136+
['used', used],
137+
['available', available]
140138
],
141-
// https://www.patternfly.org/styles/color-palette/
142139
colors: {
143-
// Orange if at quota, blue otherwise
144-
Used: available ? "#0088ce" : "#ec7a08",
145-
// Gray
146-
Available: "#d1d1d1"
140+
used: available ? "#0088ce" : "#ec7a08",
141+
other: available ? "#7dc3e8" : "#f7bd7f",
142+
available: "#d1d1d1"
143+
},
144+
names: {
145+
used: hasCrossProject ? 'Used - This Project' : 'Used',
146+
other: 'Used - Other Projects',
147+
available: "Available"
147148
}
148149
};
149150

151+
if (hasCrossProject) {
152+
data.columns.splice(1, 0, ['other', crossProjectUsed]);
153+
}
154+
150155
if (!chart) {
151156
_.assign(config.data, data);
152157
chart = c3.generate(config);
153158
} else {
154159
chart.load(data);
155160
}
156161
};
157-
$scope.$watchGroup(['used', 'total'], _.debounce(updateChart, 300));
162+
$scope.$watchGroup(['used', 'total', 'crossProjectUsed'], _.debounce(updateChart, 300));
158163
}
159164
};
160165
});

app/scripts/filters/resources.js

+11
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,17 @@ angular.module('openshiftConsole')
11641164
});
11651165
};
11661166
})
1167+
.filter('scopeDetails', function() {
1168+
var scopeMessages = {
1169+
"Terminating": "Matches pods that have an active deadline.",
1170+
"NotTerminating": "Matches pods that do not have an active deadline.",
1171+
"BestEffort": "Matches pods that have best effort quality of service.",
1172+
"NotBestEffort": "Matches pods that do not have best effort quality of service."
1173+
};
1174+
return function(scope) {
1175+
return scopeMessages[scope];
1176+
};
1177+
})
11671178
.filter('debugLabel', function(PodsService) {
11681179
return function(pod) {
11691180
return PodsService.getDebugLabel(pod);

app/views/settings.html

+99-28
Original file line numberDiff line numberDiff line change
@@ -57,71 +57,142 @@ <h1>
5757
</div><!-- /resource details -->
5858

5959

60-
<div ng-if="(quotas | hashSize) === 0">
60+
<div ng-if="!(quotas | hashSize) && !(clusterQuotas | hashSize)">
6161
<h2>
6262
<span>Quota</span>
6363
</h2>
6464
<div class="help-block">{{quotaHelp}}</div>
65-
<p><em>{{emptyMessageQuotas}}</em></p>
65+
<p><em ng-if="!quotas && !clusterQuotas">Loading...</em><em ng-if="quotas || clusterQuotas">There are no resource quotas set on this project.</em></p>
66+
</div>
67+
68+
<div ng-repeat="quota in clusterQuotas | orderBy: 'metadata.name'" class="gutter-bottom">
69+
<h2>
70+
Cluster Quota <span ng-if="(clusterQuotas | hashSize) > 1">{{quota.metadata.name}}</span>
71+
</h2>
72+
<div ng-if="$first" class="help-block">Limits resource usage across a set of projects.</div>
73+
<dl ng-if="quota.spec.quota.scopes.length">
74+
<dt>Scopes:</dt>
75+
<dd>
76+
<div ng-repeat="scope in quota.spec.quota.scopes">
77+
{{scope | startCase}}
78+
<span class="text-muted small" ng-if="scope | scopeDetails">&mdash; {{scope | scopeDetails}}</span>
79+
</div>
80+
</dd>
81+
</dl>
82+
<div>
83+
<div row wrap style="justify-content: center;">
84+
<div ng-if="quota.status.total.hard.cpu" class="mar-lg">
85+
<h3 class="text-center">CPU <small>Request</small></h3>
86+
<quota-usage-chart height="240" used="namespaceUsageByClusterQuota[quota.metadata.name].used.cpu" total="quota.status.total.hard.cpu" cross-project-used="quota.status.total.used.cpu" type="cpu" class="quota-chart"></quota-usage-chart>
87+
</div>
88+
<div ng-if="quota.status.total.hard.memory" class="mar-lg">
89+
<h3 class="text-center">Memory <small>Request</small></h3>
90+
<quota-usage-chart height="240" used="namespaceUsageByClusterQuota[quota.metadata.name].used.memory" cross-project-used="quota.status.total.used.memory" total="quota.status.total.hard.memory" type="memory" class="quota-chart"></quota-usage-chart>
91+
</div>
92+
<div ng-if="quota.status.total.hard['requests.cpu']" class="mar-lg">
93+
<h3 class="text-center">CPU <small>Request</small></h3>
94+
<quota-usage-chart height="240" used="namespaceUsageByClusterQuota[quota.metadata.name].used['requests.cpu']" cross-project-used="quota.status.total.used['requests.cpu']" total="quota.status.total.hard['requests.cpu']" type="cpu" class="quota-chart"></quota-usage-chart>
95+
</div>
96+
<div ng-if="quota.status.total.hard['requests.memory']" class="mar-lg">
97+
<h3 class="text-center">Memory <small>Request</small></h3>
98+
<quota-usage-chart height="240" used="namespaceUsageByClusterQuota[quota.metadata.name].used['requests.memory']" cross-project-used="quota.status.total.used['requests.memory']" total="quota.status.total.hard['requests.memory']" type="memory" class="quota-chart"></quota-usage-chart>
99+
</div>
100+
<div ng-if="quota.status.total.hard['limits.cpu']" class="mar-lg">
101+
<h3 class="text-center">CPU <small>Limit</small></h3>
102+
<quota-usage-chart height="240" used="namespaceUsageByClusterQuota[quota.metadata.name].used['limits.cpu']" cross-project-used="quota.status.total.used['limits.cpu']" total="quota.status.total.hard['limits.cpu']" type="cpu" class="quota-chart"></quota-usage-chart>
103+
</div>
104+
<div ng-if="quota.status.total.hard['limits.memory']" class="mar-lg">
105+
<h3 class="text-center">Memory <small>Limit</small></h3>
106+
<quota-usage-chart height="240" used="namespaceUsageByClusterQuota[quota.metadata.name].used['limits.memory']" cross-project-used="quota.status.total.used['limits.memory']" total="quota.status.total.hard['limits.memory']" type="memory" class="quota-chart"></quota-usage-chart>
107+
</div>
108+
</div>
109+
</div>
110+
111+
<div class="table-responsive">
112+
<table class="table">
113+
<thead>
114+
<th>Resource type</th>
115+
<th>Used (this project)</th>
116+
<th>Used (all projects)</th>
117+
<th>Max</th>
118+
</thead>
119+
<tbody>
120+
<tr ng-if='!quota.status.total.used' class="danger">
121+
<td colspan="5">
122+
<span data-toggle="tooltip" title="Missing quota status" class="pficon pficon-error-circle-o" style="cursor: help;"></span>
123+
Status has not been reported on this quota usage record. Any resources limited by this quota record can not be allocated.
124+
</td>
125+
</tr>
126+
<!-- Don't show quotas for type `resourcequotas`. They are frequently at limit,
127+
which is not something to worry about, and only a cluster admin can create
128+
those resources anyway. -->
129+
<tr ng-repeat="(resourceType, specMax) in quota.spec.quota.hard"
130+
ng-if="resourceType !== 'resourcequotas'"
131+
ng-class="{
132+
warning: (quota.status.total.used[resourceType] | usageValue) >= (quota.status.total.hard[resourceType] | usageValue)
133+
}">
134+
<td>
135+
{{resourceType | humanizeQuotaResource}}
136+
<span ng-if="(quota.status.total.used[resourceType] | usageValue) >= (quota.status.total.hard[resourceType] | usageValue)" data-toggle="tooltip" title="Quota limit reached" class="pficon pficon-warning-triangle-o" style="cursor: help; vertical-align: middle;"></span>
137+
</td>
138+
<td>
139+
<span ng-if="!namespaceUsageByClusterQuota[quota.metadata.name].used">&mdash;</span>
140+
<span ng-if="namespaceUsageByClusterQuota[quota.metadata.name].used">{{namespaceUsageByClusterQuota[quota.metadata.name].used[resourceType] | usageWithUnits : resourceType}}</span>
141+
</td>
142+
<td>
143+
<span ng-if="!quota.status.total.used">&mdash;</span>
144+
<span ng-if="quota.status.total.used">{{quota.status.total.used[resourceType] | usageWithUnits : resourceType}}</span>
145+
</td>
146+
<td>
147+
<span ng-if="!quota.status.total.hard">{{specMax | usageWithUnits : resourceType}}</span>
148+
<span ng-if="quota.status.total.hard">{{quota.status.total.hard[resourceType] | usageWithUnits : resourceType}}</span>
149+
</td>
150+
</tr>
151+
</tbody>
152+
</table>
153+
</div>
66154
</div>
67155

68156
<div ng-repeat="quota in quotas | orderBy: 'metadata.name'" class="gutter-bottom">
69157
<h2>
70-
Quota <span ng-if="(quotas | hashSize) > 1">{{quota.metadata.name}}</span>
158+
<span ng-if="(clusterQuotas | hashSize)">Project </span>Quota <span ng-if="(quotas | hashSize) > 1">{{quota.metadata.name}}</span>
71159
</h2>
72160
<div ng-if="$first" class="help-block">{{quotaHelp}}</div>
73161
<dl ng-if="quota.spec.scopes.length">
74162
<dt>Scopes:</dt>
75163
<dd>
76164
<div ng-repeat="scope in quota.spec.scopes">
77165
{{scope | startCase}}
78-
<span class="text-muted small">
79-
<span ng-switch="scope" class="hide-ng-leave">
80-
<span ng-switch-when="Terminating">&mdash; Matches pods that have an active deadline.</span>
81-
<span ng-switch-when="NotTerminating">&mdash; Matches pods that do not have an active deadline.</span>
82-
<span ng-switch-when="BestEffort">&mdash; Matches pods that have best effort quality of service.</span>
83-
<span ng-switch-when="NotBestEffort">&mdash; Matches pods that do not have best effort quality of service.</span>
84-
</span>
85-
</span>
166+
<span class="text-muted small" ng-if="scope | scopeDetails">&mdash; {{scope | scopeDetails}}</span>
86167
</div>
87168
</dd>
88169
</dl>
89170
<div>
90-
<div row wrap mobile="column">
91-
<div flex></div>
92-
<div column ng-if="quota.status.hard.cpu" class="center-block gutter-bottom">
171+
<div row wrap style="justify-content: center;">
172+
<div column ng-if="quota.status.hard.cpu" class="mar-lg">
93173
<h3 class="text-center">CPU <small>Request</small></h3>
94174
<quota-usage-chart used="quota.status.used.cpu" total="quota.status.hard.cpu" type="cpu" class="quota-chart"></quota-usage-chart>
95175
</div>
96-
<div column ng-if="quota.status.hard.memory" class="center-block gutter-bottom">
176+
<div column ng-if="quota.status.hard.memory" class="mar-lg">
97177
<h3 class="text-center">Memory <small>Request</small></h3>
98178
<quota-usage-chart used="quota.status.used.memory" total="quota.status.hard.memory" type="memory" class="quota-chart"></quota-usage-chart>
99179
</div>
100-
<div flex></div>
101-
</div>
102-
<div row wrap mobile="column">
103-
<div flex></div>
104-
<div column ng-if="quota.status.hard['requests.cpu']" class="center-block gutter-bottom">
180+
<div column ng-if="quota.status.hard['requests.cpu']" class="mar-lg">
105181
<h3 class="text-center">CPU <small>Request</small></h3>
106182
<quota-usage-chart used="quota.status.used['requests.cpu']" total="quota.status.hard['requests.cpu']" type="cpu" class="quota-chart"></quota-usage-chart>
107183
</div>
108-
<div column ng-if="quota.status.hard['requests.memory']" class="center-block gutter-bottom">
184+
<div column ng-if="quota.status.hard['requests.memory']" class="mar-lg">
109185
<h3 class="text-center">Memory <small>Request</small></h3>
110186
<quota-usage-chart used="quota.status.used['requests.memory']" total="quota.status.hard['requests.memory']" type="memory" class="quota-chart"></quota-usage-chart>
111187
</div>
112-
<div flex></div>
113-
</div>
114-
<div row wrap mobile="column">
115-
<div flex></div>
116-
<div column ng-if="quota.status.hard['limits.cpu']" class="center-block gutter-bottom">
188+
<div ng-if="quota.status.hard['limits.cpu']" class="mar-lg">
117189
<h3 class="text-center">CPU <small>Limit</small></h3>
118190
<quota-usage-chart used="quota.status.used['limits.cpu']" total="quota.status.hard['limits.cpu']" type="cpu" class="quota-chart"></quota-usage-chart>
119191
</div>
120-
<div column ng-if="quota.status.hard['limits.memory']" class="center-block gutter-bottom">
192+
<div ng-if="quota.status.hard['limits.memory']" class="mar-lg">
121193
<h3 class="text-center">Memory <small>Limit</small></h3>
122194
<quota-usage-chart used="quota.status.used['limits.memory']" total="quota.status.hard['limits.memory']" type="memory" class="quota-chart"></quota-usage-chart>
123195
</div>
124-
<div flex></div>
125196
</div>
126197
</div>
127198

0 commit comments

Comments
 (0)