diff --git a/data/model/legacy.py b/data/model/legacy.py
index 3a1e55526..e0e50cb67 100644
--- a/data/model/legacy.py
+++ b/data/model/legacy.py
@@ -2210,6 +2210,14 @@ def confirm_team_invite(code, user):
found.delete_instance()
return (team, inviter)
+
+def get_repository_usage():
+ repo_pull = LogEntryKind.get(name = 'pull_repo')
+ return (LogEntry.select().where(LogEntry.kind == repo_pull, ~(LogEntry.repository >> None))
+ .group_by(LogEntry.ip)
+ .group_by(LogEntry.repository)
+ .count())
+
def archivable_buildlogs_query():
presumed_dead_date = datetime.utcnow() - PRESUMED_DEAD_BUILD_AGE
return (RepositoryBuild.select()
diff --git a/endpoints/api/superuser.py b/endpoints/api/superuser.py
index c41c6a46c..24472b1b8 100644
--- a/endpoints/api/superuser.py
+++ b/endpoints/api/superuser.py
@@ -52,6 +52,25 @@ def user_view(user):
'super_user': user.username in app.config['SUPER_USERS']
}
+@resource('/v1/superuser/usage/')
+@internal_only
+@show_if(features.SUPER_USERS)
+class UsageInformation(ApiResource):
+ """ Resource for returning the usage information for enterprise customers. """
+ @require_fresh_login
+ @nickname('getSystemUsage')
+ def get(self):
+ """ Returns the number of repository handles currently held. """
+ if SuperUserPermission().can():
+ return {
+ 'usage': model.get_repository_usage(),
+ 'allowed': 0
+ }
+
+ abort(403)
+
+
+
@resource('/v1/superuser/users/')
@internal_only
@show_if(features.SUPER_USERS)
diff --git a/static/css/quay.css b/static/css/quay.css
index 7f711f79a..25934010b 100644
--- a/static/css/quay.css
+++ b/static/css/quay.css
@@ -3131,38 +3131,38 @@ p.editable:hover i {
stroke-width: 1.5px;
}
-.usage-chart {
+.usage-chart-element {
display: inline-block;
vertical-align: middle;
width: 200px;
height: 200px;
}
-.usage-chart .count-text {
+.usage-chart-element .count-text {
font-size: 22px;
}
-.usage-chart.limit-at path.arc-0 {
+.usage-chart-element.limit-at path.arc-0 {
fill: #c09853;
}
-.usage-chart.limit-over path.arc-0 {
+.usage-chart-element.limit-over path.arc-0 {
fill: #b94a48;
}
-.usage-chart.limit-near path.arc-0 {
+.usage-chart-element.limit-near path.arc-0 {
fill: #468847;
}
-.usage-chart.limit-over path.arc-1 {
+.usage-chart-element.limit-over path.arc-1 {
fill: #fcf8e3;
}
-.usage-chart.limit-at path.arc-1 {
+.usage-chart-element.limit-at path.arc-1 {
fill: #f2dede;
}
-.usage-chart.limit-near path.arc-1 {
+.usage-chart-element.limit-near path.arc-1 {
fill: #dff0d8;
}
diff --git a/static/directives/plan-manager.html b/static/directives/plan-manager.html
index af4c3c016..e15e887be 100644
--- a/static/directives/plan-manager.html
+++ b/static/directives/plan-manager.html
@@ -24,10 +24,11 @@
-
+
diff --git a/static/directives/usage-chart.html b/static/directives/usage-chart.html
new file mode 100644
index 000000000..9b9782138
--- /dev/null
+++ b/static/directives/usage-chart.html
@@ -0,0 +1,2 @@
+
+{{ usageTitle }}
diff --git a/static/js/app.js b/static/js/app.js
index f751cdd98..d76fcc549 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -4540,23 +4540,6 @@ quayApp.directive('planManager', function () {
$scope.planChanged({ 'plan': subscribedPlan });
}
- if (sub.usedPrivateRepos > $scope.subscribedPlan.privateRepos) {
- $scope.limit = 'over';
- } else if (sub.usedPrivateRepos == $scope.subscribedPlan.privateRepos) {
- $scope.limit = 'at';
- } else if (sub.usedPrivateRepos >= $scope.subscribedPlan.privateRepos * 0.7) {
- $scope.limit = 'near';
- } else {
- $scope.limit = 'none';
- }
-
- if (!$scope.chart) {
- $scope.chart = new UsageChart();
- $scope.chart.draw('repository-usage-chart');
- }
-
- $scope.chart.update(sub.usedPrivateRepos || 0, $scope.subscribedPlan.privateRepos || 0);
-
$scope.planChanging = false;
$scope.planLoading = false;
});
@@ -5929,6 +5912,54 @@ quayApp.directive('notificationsBubble', function () {
});
+quayApp.directive('usageChart', function () {
+ var directiveDefinitionObject = {
+ priority: 0,
+ templateUrl: '/static/directives/usage-chart.html',
+ replace: false,
+ transclude: false,
+ restrict: 'C',
+ scope: {
+ 'current': '=current',
+ 'total': '=total',
+ 'limit': '=limit',
+ 'usageTitle': '@usageTitle'
+ },
+ controller: function($scope, $element) {
+ $scope.limit = "";
+
+ var chart = null;
+
+ var update = function() {
+ if ($scope.current == null || $scope.total == null) { return; }
+ if (!chart) {
+ chart = new UsageChart();
+ chart.draw('usage-chart-element');
+ }
+
+ var current = $scope.current || 0;
+ var total = $scope.total || 0;
+ if (current > total) {
+ $scope.limit = 'over';
+ } else if (current == total) {
+ $scope.limit = 'at';
+ } else if (current >= total * 0.7) {
+ $scope.limit = 'near';
+ } else {
+ $scope.limit = 'none';
+ }
+
+ chart.update($scope.current, $scope.total);
+ };
+
+ $scope.$watch('current', update);
+ $scope.$watch('total', update);
+ }
+ };
+ return directiveDefinitionObject;
+});
+
+
quayApp.directive('notificationView', function () {
var directiveDefinitionObject = {
priority: 0,
diff --git a/static/js/controllers.js b/static/js/controllers.js
index ffec020b7..d28181b7c 100644
--- a/static/js/controllers.js
+++ b/static/js/controllers.js
@@ -2788,6 +2788,15 @@ function SuperUserAdminCtrl($scope, ApiService, Features, UserService) {
$scope.logsCounter = 0;
$scope.newUser = {};
$scope.createdUsers = [];
+ $scope.systemUsage = null;
+
+ $scope.getUsage = function() {
+ if ($scope.systemUsage) { return; }
+
+ ApiService.getSystemUsage().then(function(resp) {
+ $scope.systemUsage = resp;
+ }, ApiService.errorDisplay('Cannot load system usage. Please contact support.'))
+ }
$scope.loadLogs = function() {
$scope.logsCounter++;
diff --git a/static/partials/super-user.html b/static/partials/super-user.html
index a12f9f5b2..0340bffc3 100644
--- a/static/partials/super-user.html
+++ b/static/partials/super-user.html
@@ -13,6 +13,9 @@
Create User
+
+ System Usage
+
System Logs
@@ -27,6 +30,13 @@
+
+
+