Get dashboard working and upgrade bootstrap. Note: the bootstrap fixes will be coming in the followup CL
This commit is contained in:
parent
79f39697fe
commit
524705b88c
18 changed files with 429 additions and 260 deletions
|
@ -25,6 +25,9 @@ class DynamicRegistrationComponent(BaseComponent):
|
|||
logger.debug('Registering new build component+worker with realm %s', realm)
|
||||
return realm
|
||||
|
||||
def kind(self):
|
||||
return 'registration'
|
||||
|
||||
|
||||
class EnterpriseManager(BaseManager):
|
||||
""" Build manager implementation for the Enterprise Registry. """
|
||||
|
@ -82,5 +85,7 @@ class EnterpriseManager(BaseManager):
|
|||
if build_component in self.ready_components:
|
||||
self.ready_components.remove(build_component)
|
||||
|
||||
self.unregister_component(build_component)
|
||||
|
||||
def num_workers(self):
|
||||
return len(self.all_components)
|
||||
|
|
|
@ -271,8 +271,6 @@ class EphemeralBuilderManager(BaseManager):
|
|||
|
||||
def build_component_disposed(self, build_component, timed_out):
|
||||
logger.debug('Calling build_component_disposed.')
|
||||
|
||||
# TODO make it so that I don't have to unregister the component if it timed out
|
||||
self.unregister_component(build_component)
|
||||
|
||||
@coroutine
|
||||
|
|
|
@ -70,7 +70,8 @@ class BuilderServer(object):
|
|||
|
||||
@controller_app.route('/status')
|
||||
def status():
|
||||
(running_count, available_count) = server._queue.get_metrics()
|
||||
metrics = server._queue.get_metrics(require_transaction=False)
|
||||
(running_count, available_not_running_count, available_count) = metrics
|
||||
|
||||
workers = [component for component in server._current_components
|
||||
if component.kind() == 'builder']
|
||||
|
@ -80,7 +81,7 @@ class BuilderServer(object):
|
|||
'running_local': server._job_count,
|
||||
'running_total': running_count,
|
||||
'workers': len(workers),
|
||||
'job_total': available_count
|
||||
'job_total': available_count + running_count
|
||||
}
|
||||
|
||||
return json.dumps(data)
|
||||
|
|
|
@ -6,6 +6,12 @@ from util.morecollections import AttrDict
|
|||
|
||||
MINIMUM_EXTENSION = timedelta(seconds=20)
|
||||
|
||||
class NoopWith:
|
||||
def __enter__(self):
|
||||
pass
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
pass
|
||||
|
||||
class WorkQueue(object):
|
||||
def __init__(self, queue_name, transaction_factory,
|
||||
|
@ -49,21 +55,32 @@ class WorkQueue(object):
|
|||
def _item_by_id_for_update(self, queue_id):
|
||||
return db_for_update(QueueItem.select().where(QueueItem.id == queue_id)).get()
|
||||
|
||||
def update_metrics(self):
|
||||
if self._reporter is None:
|
||||
return
|
||||
|
||||
with self._transaction_factory(db):
|
||||
def get_metrics(self, require_transaction=True):
|
||||
guard = self._transaction_factory(db) if require_transaction else NoopWith()
|
||||
with guard:
|
||||
now = datetime.utcnow()
|
||||
name_match_query = self._name_match_query()
|
||||
|
||||
running_query = self._running_jobs(now, name_match_query)
|
||||
running_count = running_query.distinct().count()
|
||||
|
||||
available_query = self._available_jobs_not_running(now, name_match_query, running_query)
|
||||
available_query = self._available_jobs(now, name_match_query)
|
||||
available_count = available_query.select(QueueItem.queue_name).distinct().count()
|
||||
|
||||
self._reporter(self._currently_processing, running_count, running_count + available_count)
|
||||
available_not_running_query = self._available_jobs_not_running(now, name_match_query,
|
||||
running_query)
|
||||
available_not_running_count = (available_not_running_query.select(QueueItem.queue_name)
|
||||
.distinct().count())
|
||||
|
||||
return (running_count, available_not_running_count, available_count)
|
||||
|
||||
def update_metrics(self):
|
||||
if self._reporter is None:
|
||||
return
|
||||
|
||||
(running_count, available_not_running_count, available_count) = self.get_metrics()
|
||||
self._reporter(self._currently_processing, running_count,
|
||||
running_count + available_not_running_count)
|
||||
|
||||
def put(self, canonical_name_list, message, available_after=0, retries_remaining=5):
|
||||
"""
|
||||
|
|
|
@ -50,10 +50,6 @@ class SuperUserRegistryStatus(ApiResource):
|
|||
@verify_not_prod
|
||||
def get(self):
|
||||
""" Returns the status of the registry. """
|
||||
if app.config.get('DEBUGGING', False):
|
||||
return {
|
||||
'status': 'ready'
|
||||
}
|
||||
|
||||
# If there is no conf/stack volume, then report that status.
|
||||
if not CONFIG_PROVIDER.volume_exists():
|
||||
|
|
|
@ -47,9 +47,12 @@ def ps():
|
|||
}
|
||||
json_string = json.dumps(data)
|
||||
yield 'data: %s\n\n' % json_string
|
||||
time.sleep(0.25)
|
||||
time.sleep(1)
|
||||
|
||||
try:
|
||||
return Response(generator(), mimetype="text/event-stream")
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ LOCAL_DIRECTORY = 'static/ldn/'
|
|||
|
||||
EXTERNAL_JS = [
|
||||
'code.jquery.com/jquery.js',
|
||||
'netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js',
|
||||
'netdna.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-route.min.js',
|
||||
'ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular-sanitize.min.js',
|
||||
|
@ -19,7 +19,7 @@ EXTERNAL_JS = [
|
|||
|
||||
EXTERNAL_CSS = [
|
||||
'netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.css',
|
||||
'netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.no-icons.min.css',
|
||||
'netdna.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css',
|
||||
'fonts.googleapis.com/css?family=Source+Sans+Pro:400,700',
|
||||
]
|
||||
|
||||
|
|
|
@ -705,5 +705,11 @@
|
|||
}
|
||||
|
||||
.realtime-area-chart, .realtime-line-chart {
|
||||
display: inline-block;
|
||||
margin: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rickshaw_graph {
|
||||
overflow: hidden;
|
||||
padding-bottom: 40px;
|
||||
}
|
|
@ -4972,3 +4972,9 @@ i.slack-icon {
|
|||
left: 16px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.chart-col h4, .chart-col h5 {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="config-setup-tool-element">
|
||||
<div class="quay-spinner" ng-if="!config"></div>
|
||||
<div class="cor-loader" ng-if="!config"></div>
|
||||
<div ng-show="config && config['SUPER_USERS']">
|
||||
<form id="configform" name="configform">
|
||||
|
||||
|
@ -289,7 +289,7 @@
|
|||
<div class="description">
|
||||
<p>
|
||||
Authentication for the registry can be handled by either the registry itself or LDAP.
|
||||
External authentication providers (such as Github) can be used on top of this choice.
|
||||
External authentication providers (such as GitHub) can be used on top of this choice.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -339,20 +339,20 @@
|
|||
</div>
|
||||
</div> <!-- /Authentication -->
|
||||
|
||||
<!-- Github Authentication -->
|
||||
<!-- GitHub Authentication -->
|
||||
<div class="co-panel">
|
||||
<div class="co-panel-heading">
|
||||
<i class="fa fa-github"></i> Github (Enterprise) Authentication
|
||||
<i class="fa fa-github"></i> GitHub (Enterprise) Authentication
|
||||
</div>
|
||||
<div class="co-panel-body">
|
||||
<div class="description">
|
||||
<p>
|
||||
If enabled, users can use Github or Github Enterprise to authenticate to the registry.
|
||||
If enabled, users can use GitHub or GitHub Enterprise to authenticate to the registry.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Note:</strong> A registered Github (Enterprise) OAuth application is required.
|
||||
<strong>Note:</strong> A registered GitHub (Enterprise) OAuth application is required.
|
||||
View instructions on how to
|
||||
<a href="https://coreos.com/docs/enterprise-registry/github-auth/" target="_blank">
|
||||
<a href="https://coreos.com/docs/enterprise-registry/github-app/" target="_blank">
|
||||
Create an OAuth Application in GitHub
|
||||
</a>
|
||||
</p>
|
||||
|
@ -360,21 +360,21 @@
|
|||
|
||||
<div class="co-checkbox">
|
||||
<input id="ftghl" type="checkbox" ng-model="config.FEATURE_GITHUB_LOGIN">
|
||||
<label for="ftghl">Enable Github Authentication</label>
|
||||
<label for="ftghl">Enable GitHub Authentication</label>
|
||||
</div>
|
||||
|
||||
<table class="config-table" ng-if="config.FEATURE_GITHUB_LOGIN">
|
||||
<tr>
|
||||
<td>Github:</td>
|
||||
<td>GitHub:</td>
|
||||
<td>
|
||||
<select ng-model="mapped.GITHUB_LOGIN_KIND">
|
||||
<option value="hosted">Github.com</option>
|
||||
<option value="enterprise">Github Enterprise</option>
|
||||
<option value="hosted">GitHub.com</option>
|
||||
<option value="enterprise">GitHub Enterprise</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="mapped.GITHUB_LOGIN_KIND == 'enterprise'">
|
||||
<td>Github Endpoint:</td>
|
||||
<td>GitHub Endpoint:</td>
|
||||
<td>
|
||||
<span class="config-string-field"
|
||||
binding="config.GITHUB_LOGIN_CONFIG.GITHUB_ENDPOINT"
|
||||
|
@ -382,7 +382,7 @@
|
|||
pattern="{{ GITHUB_REGEX }}">
|
||||
</span>
|
||||
<div class="help-text">
|
||||
The Github Enterprise endpoint. Must start with http:// or https://.
|
||||
The GitHub Enterprise endpoint. Must start with http:// or https://.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -402,7 +402,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div> <!-- /Github Authentication -->
|
||||
</div> <!-- /GitHub Authentication -->
|
||||
|
||||
<!-- Google Authentication -->
|
||||
<div class="co-panel">
|
||||
|
@ -471,20 +471,20 @@
|
|||
</div> <!-- /Build Support -->
|
||||
|
||||
|
||||
<!-- Github Trigger -->
|
||||
<!-- GitHub Trigger -->
|
||||
<div class="co-panel" ng-if="config.FEATURE_BUILD_SUPPORT" style="margin-top: 20px;">
|
||||
<div class="co-panel-heading">
|
||||
<i class="fa fa-github"></i> Github (Enterprise) Build Triggers
|
||||
<i class="fa fa-github"></i> GitHub (Enterprise) Build Triggers
|
||||
</div>
|
||||
<div class="co-panel-body">
|
||||
<div class="description">
|
||||
<p>
|
||||
If enabled, users can setup Github or Github Enterprise triggers to invoke Registry builds.
|
||||
If enabled, users can setup GitHub or GitHub Enterprise triggers to invoke Registry builds.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Note:</strong> A registered Github (Enterprise) OAuth application (<strong>separate from Github Authentication</strong>) is required.
|
||||
<strong>Note:</strong> A registered GitHub (Enterprise) OAuth application (<strong>separate from GitHub Authentication</strong>) is required.
|
||||
View instructions on how to
|
||||
<a href="https://coreos.com/docs/enterprise-registry/github-auth/" target="_blank">
|
||||
<a href="https://coreos.com/docs/enterprise-registry/github-app/" target="_blank">
|
||||
Create an OAuth Application in GitHub
|
||||
</a>
|
||||
</p>
|
||||
|
@ -492,21 +492,21 @@
|
|||
|
||||
<div class="co-checkbox">
|
||||
<input id="ftgb" type="checkbox" ng-model="config.FEATURE_GITHUB_BUILD">
|
||||
<label for="ftgb">Enable Github Triggers</label>
|
||||
<label for="ftgb">Enable GitHub Triggers</label>
|
||||
</div>
|
||||
|
||||
<table class="config-table" ng-if="config.FEATURE_GITHUB_BUILD">
|
||||
<tr>
|
||||
<td>Github:</td>
|
||||
<td>GitHub:</td>
|
||||
<td>
|
||||
<select ng-model="mapped.GITHUB_TRIGGER_KIND">
|
||||
<option value="hosted">Github.com</option>
|
||||
<option value="enterprise">Github Enterprise</option>
|
||||
<option value="hosted">GitHub.com</option>
|
||||
<option value="enterprise">GitHub Enterprise</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="mapped.GITHUB_TRIGGER_KIND == 'enterprise'">
|
||||
<td>Github Endpoint:</td>
|
||||
<td>GitHub Endpoint:</td>
|
||||
<td>
|
||||
<span class="config-string-field"
|
||||
binding="config.GITHUB_TRIGGER_CONFIG.GITHUB_ENDPOINT"
|
||||
|
@ -514,7 +514,7 @@
|
|||
pattern="{{ GITHUB_REGEX }}">
|
||||
</span>
|
||||
<div class="help-text">
|
||||
The Github Enterprise endpoint. Must start with http:// or https://.
|
||||
The GitHub Enterprise endpoint. Must start with http:// or https://.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -534,7 +534,7 @@
|
|||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div> <!-- /Github Trigger -->
|
||||
</div> <!-- /GitHub Trigger -->
|
||||
</form>
|
||||
|
||||
<!-- Save Bar -->
|
||||
|
|
|
@ -1,39 +1,75 @@
|
|||
<div class="ps-usage-graph-element">
|
||||
<div quay-show="Features.BUILD_SUPPORT" ng-if="data.build && data.build.job_total">
|
||||
Build Workers:
|
||||
<!-- Build Charts -->
|
||||
<div quay-show="Features.BUILD_SUPPORT">
|
||||
<div class="alert alert-warning" ng-if="data.build && data.build.job_total == null">
|
||||
Cannot load build system status. Please restart your container.
|
||||
</div>
|
||||
<div ng-if="data.build && data.build.job_total >= 0">
|
||||
<div class="col-md-6 chart-col">
|
||||
<h4>Build Queue</h4>
|
||||
<h5>
|
||||
Running Jobs: {{ data.build.running_total }} | Total Jobs: {{ data.build.job_total }}
|
||||
</h5>
|
||||
<div class="realtime-area-chart"
|
||||
data="[data.build.job_total, data.build.running_total]"
|
||||
labels="['Queued Build Jobs', 'Running Build Jobs']"
|
||||
colors="['rgb(157, 194, 211)', 'rgb(56, 122, 163)']"
|
||||
counter="counter"></div>
|
||||
counter="counter"
|
||||
minimum="-10"
|
||||
maximum="auto"></div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 chart-col">
|
||||
<h4>Local Build Workers</h4>
|
||||
<h5>
|
||||
Local Workers: {{ data.build.workers }} | Working: {{ data.build.running_local }}
|
||||
</h5>
|
||||
<div class="realtime-area-chart"
|
||||
data="[data.build.job_total, data.build.workers, data.build.running_local]"
|
||||
labels="['Queued Build Jobs', 'Build Workers (local)', 'Running Build Jobs (local)']"
|
||||
colors="['rgb(157, 194, 211)', 'rgb(161, 208, 93)', 'rgb(210, 237, 130)']"
|
||||
counter="counter"></div>
|
||||
counter="counter"
|
||||
minimum="-10"
|
||||
maximum="auto"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
CPU:
|
||||
<!-- CPU, Memory and Network -->
|
||||
<div class="col-md-4 chart-col">
|
||||
<h4>CPU Usage %</h4>
|
||||
<div class="realtime-line-chart" data="data.count.cpu" counter="counter"
|
||||
label-template="CPU #{x} %"></div>
|
||||
label-template="CPU #{x} %"
|
||||
minimum="-10" maximum="110"></div>
|
||||
</div>
|
||||
|
||||
Process Count:
|
||||
<div class="col-md-4 chart-col">
|
||||
<h4>Process Count</h4>
|
||||
<div class="realtime-line-chart" data="data.count.processes" counter="counter"
|
||||
label-template="Process Count"></div>
|
||||
</div>
|
||||
|
||||
Virtual Memory:
|
||||
<div class="col-md-4 chart-col">
|
||||
<h4>Virtual Memory %</h4>
|
||||
<div class="realtime-line-chart" data="data.count.virtual_mem[2]" counter="counter"
|
||||
label-template="Virtual Memory %"></div>
|
||||
label-template="Virtual Memory %"
|
||||
minimum="-10" maximum="110"></div>
|
||||
</div>
|
||||
|
||||
Swap Memory:
|
||||
<div class="col-md-4 chart-col">
|
||||
<h4>Swap Memory</h4>
|
||||
<div class="realtime-line-chart" data="data.count.swap_mem[3]" counter="counter"
|
||||
label-template="Swap Memory %"></div>
|
||||
</div>
|
||||
|
||||
Network Connections:
|
||||
<div class="col-md-4 chart-col">
|
||||
<h4>Network Connections</h4>
|
||||
<div class="realtime-line-chart" data="data.count.connections" counter="counter"
|
||||
label-template="Network Connection Count"></div>
|
||||
</div>
|
||||
|
||||
Network Usage:
|
||||
<div class="col-md-4 chart-col">
|
||||
<h4>Network Usage (Bytes)</h4>
|
||||
<div class="realtime-line-chart" data="data.count.network" labels="['Bytes In', 'Bytes Out']" counter="counter"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,3 +1,6 @@
|
|||
<div class="realtime-area-chart-element">
|
||||
<div class="chart" style="width: 450px; height: 250px;"></div>
|
||||
<div ng-show="counter >= 1">
|
||||
<div class="chart"></div>
|
||||
</div>
|
||||
<div class="cor-loader-inline" ng-if="counter < 1"></div>
|
||||
</div>
|
|
@ -1,3 +1,6 @@
|
|||
<div class="realtime-line-chart-element">
|
||||
<div class="chart" style="width: 450px; height: 250px;"></div>
|
||||
<div ng-show="counter >= 1">
|
||||
<div class="chart"></div>
|
||||
</div>
|
||||
<div class="cor-loader-inline" ng-if="counter < 1"></div>
|
||||
</div>
|
196
static/js/app.js
196
static/js/app.js
|
@ -6602,178 +6602,6 @@ quayApp.directive('locationView', function () {
|
|||
});
|
||||
|
||||
|
||||
quayApp.directive('realtimeAreaChart', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/realtime-area-chart.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'data': '=data',
|
||||
'labels': '=labels',
|
||||
'colors': '=colors',
|
||||
'counter': '=counter'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
var graph = null;
|
||||
var series = [];
|
||||
var palette = new Rickshaw.Color.Palette( { scheme: 'spectrum14' } );
|
||||
var colors = $scope.colors || [];
|
||||
|
||||
var setupGraph = function() {
|
||||
for (var i = 0; i < $scope.labels.length; ++i) {
|
||||
series.push({
|
||||
name: $scope.labels[i],
|
||||
color: i >= colors.length ? palette.color(): $scope.colors[i],
|
||||
stroke: 'rgba(0,0,0,0.15)',
|
||||
data: []
|
||||
});
|
||||
}
|
||||
|
||||
graph = new Rickshaw.Graph( {
|
||||
element: $element.find('.chart')[0],
|
||||
renderer: 'area',
|
||||
stroke: true,
|
||||
series: series,
|
||||
min: 0
|
||||
});
|
||||
};
|
||||
|
||||
var refresh = function(data) {
|
||||
if (!data || $scope.counter < 0) { return; }
|
||||
if (!graph) {
|
||||
setupGraph();
|
||||
}
|
||||
|
||||
for (var i = 0; i < $scope.data.length; ++i) {
|
||||
series[i].data.push(
|
||||
{'x': $scope.counter, 'y': $scope.data[i] }
|
||||
);
|
||||
}
|
||||
|
||||
hoverDetail = new Rickshaw.Graph.HoverDetail({
|
||||
graph: graph,
|
||||
xFormatter: function(x) {
|
||||
return x.toString();
|
||||
}
|
||||
});
|
||||
|
||||
graph.renderer.unstack = true;
|
||||
graph.render();
|
||||
};
|
||||
|
||||
$scope.$watch('counter', function() {
|
||||
refresh($scope.data_raw);
|
||||
});
|
||||
|
||||
$scope.$watch('data', function(data) {
|
||||
$scope.data_raw = data;
|
||||
refresh($scope.data_raw);
|
||||
});
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
|
||||
|
||||
quayApp.directive('realtimeLineChart', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/realtime-line-chart.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'data': '=data',
|
||||
'labels': '=labels',
|
||||
'counter': '=counter',
|
||||
'labelTemplate': '@labelTemplate'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
var graph = null;
|
||||
var hoverDetail = null;
|
||||
var series = [];
|
||||
var counter = 0;
|
||||
var palette = new Rickshaw.Color.Palette( { scheme: 'spectrum14' } );
|
||||
|
||||
var setupGraph = function() {
|
||||
graph = new Rickshaw.Graph({
|
||||
element: $element.find('.chart')[0],
|
||||
renderer: 'line',
|
||||
series: series,
|
||||
min: 'auto',
|
||||
padding: {
|
||||
'top': 0.1,
|
||||
'left': 0.01,
|
||||
'right': 0.01,
|
||||
'bottom': 0.1
|
||||
}
|
||||
});
|
||||
|
||||
hoverDetail = new Rickshaw.Graph.HoverDetail({
|
||||
graph: graph,
|
||||
xFormatter: function(x) {
|
||||
return x.toString();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var refresh = function(data) {
|
||||
if (!data) { return; }
|
||||
if (!graph) {
|
||||
setupGraph();
|
||||
}
|
||||
|
||||
if (typeof data == 'number') {
|
||||
data = [data];
|
||||
}
|
||||
|
||||
if ($scope.labels) {
|
||||
data = data.slice(0, $scope.labels.length);
|
||||
}
|
||||
|
||||
if (series.length == 0){
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
var title = $scope.labels ? $scope.labels[i] : $scope.labelTemplate.replace('{x}', i + 1);
|
||||
series.push({
|
||||
'color': palette.color(),
|
||||
'data': [],
|
||||
'name': title
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
counter++;
|
||||
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
var arr = series[i].data;
|
||||
arr.push({
|
||||
'x': counter,
|
||||
'y': data[i]
|
||||
})
|
||||
|
||||
if (arr.length > 10) {
|
||||
series[i].data = arr.slice(arr.length - 10, arr.length);
|
||||
}
|
||||
}
|
||||
|
||||
graph.render();
|
||||
};
|
||||
|
||||
$scope.$watch('counter', function(counter) {
|
||||
refresh($scope.data_raw);
|
||||
});
|
||||
|
||||
$scope.$watch('data', function(data) {
|
||||
$scope.data_raw = data;
|
||||
});
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
|
||||
|
||||
quayApp.directive('psUsageGraph', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
|
@ -6782,18 +6610,40 @@ quayApp.directive('psUsageGraph', function () {
|
|||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'isEnabled': '=isEnabled'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
$scope.counter = -1;
|
||||
$scope.data = null;
|
||||
|
||||
var source = new EventSource('/realtime/ps');
|
||||
var source = null;
|
||||
|
||||
var connect = function() {
|
||||
if (source) { return; }
|
||||
source = new EventSource('/realtime/ps');
|
||||
source.onmessage = function(e) {
|
||||
$scope.$apply(function() {
|
||||
$scope.counter++;
|
||||
$scope.data = JSON.parse(e.data);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
var disconnect = function() {
|
||||
if (!source) { return; }
|
||||
source.close();
|
||||
source = null;
|
||||
};
|
||||
|
||||
$scope.$watch('isEnabled', function(value) {
|
||||
if (value) {
|
||||
connect();
|
||||
} else {
|
||||
disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on("$destroy", disconnect);
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
|
|
|
@ -17,6 +17,11 @@ function SuperUserAdminCtrl($scope, $timeout, ApiService, Features, UserService,
|
|||
$scope.pollChannel = null;
|
||||
$scope.logsScrolled = false;
|
||||
$scope.csrf_token = encodeURIComponent(window.__token);
|
||||
$scope.dashboardActive = false;
|
||||
|
||||
$scope.setDashboardActive = function(active) {
|
||||
$scope.dashboardActive = active;
|
||||
};
|
||||
|
||||
$scope.configurationSaved = function() {
|
||||
$scope.requiresRestart = true;
|
||||
|
|
|
@ -307,10 +307,10 @@ angular.module("core-config-setup", ['angularFileUpload'])
|
|||
if (!value) { return; }
|
||||
|
||||
ApiService.scGetConfig().then(function(resp) {
|
||||
$scope.config = resp['config'];
|
||||
$scope.config = resp['config'] || {};
|
||||
initializeMappedLogic($scope.config);
|
||||
$scope.mapped['$hasChanges'] = false;
|
||||
});
|
||||
}, ApiService.errorDisplay('Could not load config'));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -270,9 +270,18 @@ angular.module("core-ui", [])
|
|||
'tabActive': '@tabActive',
|
||||
'tabTitle': '@tabTitle',
|
||||
'tabTarget': '@tabTarget',
|
||||
'tabInit': '&tabInit'
|
||||
'tabInit': '&tabInit',
|
||||
'tabShown': '&tabShown',
|
||||
'tabHidden': '&tabHidden'
|
||||
},
|
||||
controller: function($rootScope, $scope, $element) {
|
||||
$element.find('a[data-toggle="tab"]').on('hidden.bs.tab', function (e) {
|
||||
$scope.tabHidden({});
|
||||
});
|
||||
|
||||
$element.find('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
$scope.tabShown({});
|
||||
});
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
|
@ -297,6 +306,236 @@ angular.module("core-ui", [])
|
|||
return directiveDefinitionObject;
|
||||
})
|
||||
|
||||
.directive('realtimeAreaChart', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/realtime-area-chart.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'data': '=data',
|
||||
'labels': '=labels',
|
||||
'colors': '=colors',
|
||||
'counter': '=counter'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
var graph = null;
|
||||
var series = [];
|
||||
var palette = new Rickshaw.Color.Palette( { scheme: 'spectrum14' } );
|
||||
var colors = $scope.colors || [];
|
||||
|
||||
var setupGraph = function() {
|
||||
for (var i = 0; i < $scope.labels.length; ++i) {
|
||||
series.push({
|
||||
name: $scope.labels[i],
|
||||
color: i >= colors.length ? palette.color(): $scope.colors[i],
|
||||
stroke: 'rgba(0,0,0,0.15)',
|
||||
data: []
|
||||
});
|
||||
}
|
||||
|
||||
var options = {
|
||||
element: $element.find('.chart')[0],
|
||||
renderer: 'area',
|
||||
stroke: true,
|
||||
series: series,
|
||||
min: 0,
|
||||
padding: {
|
||||
'top': 0.3,
|
||||
'left': 0,
|
||||
'right': 0,
|
||||
'bottom': 0.3
|
||||
}
|
||||
};
|
||||
|
||||
if ($scope.minimum != null) {
|
||||
options['min'] = $scope.minimum == 'auto' ? 'auto' : $scope.minimum * 1;
|
||||
} else {
|
||||
options['min'] = 0;
|
||||
}
|
||||
|
||||
if ($scope.maximum != null) {
|
||||
options['max'] = $scope.maximum == 'auto' ? 'auto' : $scope.maximum * 1;
|
||||
}
|
||||
|
||||
graph = new Rickshaw.Graph(options);
|
||||
|
||||
xaxes = new Rickshaw.Graph.Axis.Time({
|
||||
graph: graph,
|
||||
timeFixture: new Rickshaw.Fixtures.Time.Local()
|
||||
});
|
||||
|
||||
yaxes = new Rickshaw.Graph.Axis.Y({
|
||||
graph: graph,
|
||||
tickFormat: Rickshaw.Fixtures.Number.formatKMBT
|
||||
});
|
||||
|
||||
hoverDetail = new Rickshaw.Graph.HoverDetail({
|
||||
graph: graph,
|
||||
xFormatter: function(x) {
|
||||
return new Date(x * 1000).toString();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var refresh = function(data) {
|
||||
if (!data || $scope.counter < 0) { return; }
|
||||
if (!graph) {
|
||||
setupGraph();
|
||||
}
|
||||
|
||||
var timecode = new Date().getTime() / 1000;
|
||||
for (var i = 0; i < $scope.data.length; ++i) {
|
||||
var arr = series[i].data;
|
||||
arr.push(
|
||||
{'x': timecode, 'y': $scope.data[i] }
|
||||
);
|
||||
|
||||
if (arr.length > 10) {
|
||||
series[i].data = arr.slice(arr.length - 10, arr.length);
|
||||
}
|
||||
}
|
||||
|
||||
graph.renderer.unstack = true;
|
||||
graph.update();
|
||||
};
|
||||
|
||||
$scope.$watch('counter', function() {
|
||||
refresh($scope.data_raw);
|
||||
});
|
||||
|
||||
$scope.$watch('data', function(data) {
|
||||
$scope.data_raw = data;
|
||||
refresh($scope.data_raw);
|
||||
});
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
})
|
||||
|
||||
|
||||
.directive('realtimeLineChart', function () {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 0,
|
||||
templateUrl: '/static/directives/realtime-line-chart.html',
|
||||
replace: false,
|
||||
transclude: false,
|
||||
restrict: 'C',
|
||||
scope: {
|
||||
'data': '=data',
|
||||
'labels': '=labels',
|
||||
'counter': '=counter',
|
||||
'labelTemplate': '@labelTemplate',
|
||||
'minimum': '@minimum',
|
||||
'maximum': '@maximum'
|
||||
},
|
||||
controller: function($scope, $element) {
|
||||
var graph = null;
|
||||
var xaxes = null;
|
||||
var yaxes = null;
|
||||
var hoverDetail = null;
|
||||
var series = [];
|
||||
var counter = 0;
|
||||
var palette = new Rickshaw.Color.Palette( { scheme: 'spectrum14' } );
|
||||
|
||||
var setupGraph = function() {
|
||||
var options = {
|
||||
element: $element.find('.chart')[0],
|
||||
renderer: 'line',
|
||||
series: series,
|
||||
padding: {
|
||||
'top': 0.3,
|
||||
'left': 0,
|
||||
'right': 0,
|
||||
'bottom': 0.3
|
||||
}
|
||||
};
|
||||
|
||||
if ($scope.minimum != null) {
|
||||
options['min'] = $scope.minimum == 'auto' ? 'auto' : $scope.minimum * 1;
|
||||
} else {
|
||||
options['min'] = 0;
|
||||
}
|
||||
|
||||
if ($scope.maximum != null) {
|
||||
options['max'] = $scope.maximum == 'auto' ? 'auto' : $scope.maximum * 1;
|
||||
}
|
||||
|
||||
graph = new Rickshaw.Graph(options);
|
||||
xaxes = new Rickshaw.Graph.Axis.Time({
|
||||
graph: graph,
|
||||
timeFixture: new Rickshaw.Fixtures.Time.Local()
|
||||
});
|
||||
|
||||
yaxes = new Rickshaw.Graph.Axis.Y({
|
||||
graph: graph,
|
||||
tickFormat: Rickshaw.Fixtures.Number.formatKMBT
|
||||
});
|
||||
|
||||
hoverDetail = new Rickshaw.Graph.HoverDetail({
|
||||
graph: graph,
|
||||
xFormatter: function(x) {
|
||||
return new Date(x * 1000).toString();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var refresh = function(data) {
|
||||
if (data == null) { return; }
|
||||
if (!graph) {
|
||||
setupGraph();
|
||||
}
|
||||
|
||||
if (typeof data == 'number') {
|
||||
data = [data];
|
||||
}
|
||||
|
||||
if ($scope.labels) {
|
||||
data = data.slice(0, $scope.labels.length);
|
||||
}
|
||||
|
||||
if (series.length == 0){
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
var title = $scope.labels ? $scope.labels[i] : $scope.labelTemplate.replace('{x}', i + 1);
|
||||
series.push({
|
||||
'color': palette.color(),
|
||||
'data': [],
|
||||
'name': title
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
counter++;
|
||||
var timecode = new Date().getTime() / 1000;
|
||||
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
var arr = series[i].data;
|
||||
arr.push({
|
||||
'x': timecode,
|
||||
'y': data[i]
|
||||
})
|
||||
|
||||
if (arr.length > 10) {
|
||||
series[i].data = arr.slice(arr.length - 10, arr.length);
|
||||
}
|
||||
}
|
||||
|
||||
graph.update();
|
||||
};
|
||||
|
||||
$scope.$watch('counter', function(counter) {
|
||||
refresh($scope.data_raw);
|
||||
});
|
||||
|
||||
$scope.$watch('data', function(data) {
|
||||
$scope.data_raw = data;
|
||||
});
|
||||
}
|
||||
};
|
||||
return directiveDefinitionObject;
|
||||
})
|
||||
|
||||
.directive('corStepBar', function() {
|
||||
var directiveDefinitionObject = {
|
||||
priority: 4,
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
tab-target="#users" tab-init="loadUsers()">
|
||||
<i class="fa fa-group"></i>
|
||||
</span>
|
||||
<span class="cor-tab" tab-title="Dashboard" tab-target="#dashboard">
|
||||
<span class="cor-tab" tab-title="Dashboard" tab-target="#dashboard"
|
||||
tab-shown="setDashboardActive(true)" tab-hidden="setDashboardActive(false)">
|
||||
<i class="fa fa-tachometer"></i>
|
||||
</span>
|
||||
<span class="cor-tab" tab-title="Container Usage" tab-target="#usage-counter" tab-init="getUsage()">
|
||||
|
@ -47,7 +48,7 @@
|
|||
|
||||
<!-- Dashboard tab -->
|
||||
<div id="dashboard" class="tab-pane">
|
||||
<div class="ps-usage-graph" is-enabled="true"></div>
|
||||
<div class="ps-usage-graph" is-enabled="dashboardActive"></div>
|
||||
</div>
|
||||
|
||||
<!-- Debugging tab -->
|
||||
|
|
Reference in a new issue