mirror of https://github.com/apache/cloudstack.git
CLOUDSTACK-9020: Metrics views for CloudStack UI
Implements various metrics views based on a listView based widget that has following
properties:
- vertically and horizontally scrollable with pagination/infinite scrolling
- sortable columns (client side)
- groupable/collapsible columns
- alternate row coloring
- refresh button to refresh views
- threshold table cell coloring
- panel/breadcrumb navigation
- quick view action column
- translatable labels
- sorts after metrics is refreshed, if a column was previously sorted
- sorts after adding rows on infinite scrolling if a column was pre-sorted
- Metrics views: Zones, Clusters, Hosts, Instances, Storage pools, Volumes
- Resource filtering/navigation: Zones->Clusters->Hosts->Instances->Volumes,
Storage Pool->Volumes
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
0845edce1a
commit
ad592835c8
|
|
@ -842,6 +842,41 @@ label.menu.virtual.appliances=Virtual Appliances
|
|||
label.menu.virtual.resources=Virtual Resources
|
||||
label.menu.volumes=Volumes
|
||||
label.menu.sshkeypair=SSH KeyPair
|
||||
label.metrics=Metrics
|
||||
label.metrics.allocated=Allocated
|
||||
label.metrics.clusters=Clusters
|
||||
label.metrics.cpu.allocated=CPU Allocation
|
||||
label.metrics.cpu.max.dev=Deviation
|
||||
label.metrics.cpu.total=Total
|
||||
label.metrics.cpu.usage=CPU Usage
|
||||
label.metrics.cpu.used.avg=Used
|
||||
label.metrics.disk=Disk
|
||||
label.metrics.disk.iops.total=IOPS
|
||||
label.metrics.disk.read=Read
|
||||
label.metrics.disk.size=Size
|
||||
label.metrics.disk.storagetype=Type
|
||||
label.metrics.disk.usage=Disk Usage
|
||||
label.metrics.disk.used=Used
|
||||
label.metrics.disk.total=Total
|
||||
label.metrics.disk.allocated=Allocated
|
||||
label.metrics.disk.unallocated=Unallocated
|
||||
label.metrics.disk.write=Write
|
||||
label.metrics.hosts=Hosts
|
||||
label.metrics.memory.allocated=Mem Allocation
|
||||
label.metrics.memory.max.dev=Deviation
|
||||
label.metrics.memory.total=Total
|
||||
label.metrics.memory.usage=Mem Usage
|
||||
label.metrics.memory.used.avg=Used
|
||||
label.metrics.name=Name
|
||||
label.metrics.network.usage=Network Usage
|
||||
label.metrics.network.read=Read
|
||||
label.metrics.network.write=Write
|
||||
label.metrics.num.cpu.cores=Cores
|
||||
label.metrics.property=Property
|
||||
label.metrics.scope=Scope
|
||||
label.metrics.state=State
|
||||
label.metrics.storagepool=Storage Pool
|
||||
label.metrics.vm.name=VM Name
|
||||
label.migrate.instance.to.host=Migrate instance to another host
|
||||
label.migrate.instance.to.ps=Migrate instance to another primary storage
|
||||
label.migrate.instance.to=Migrate instance to
|
||||
|
|
|
|||
|
|
@ -12495,6 +12495,22 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it
|
|||
background-position: 0px -707px;
|
||||
}
|
||||
|
||||
.viewMetrics .icon {
|
||||
background-position: -40px -32px;
|
||||
}
|
||||
|
||||
.viewMetrics:hover .icon {
|
||||
background-position: -40px -32px;
|
||||
}
|
||||
|
||||
.refreshMetrics .icon {
|
||||
background-position: 0px -62px;
|
||||
}
|
||||
|
||||
.refreshMetrics:hover .icon {
|
||||
background-position: 0px -62px;
|
||||
}
|
||||
|
||||
.attach .icon,
|
||||
.attachISO .icon,
|
||||
.attachDisk .icon,
|
||||
|
|
|
|||
|
|
@ -837,6 +837,41 @@ dictionary = {
|
|||
'label.menu.virtual.resources': '<fmt:message key="label.menu.virtual.resources" />',
|
||||
'label.menu.volumes': '<fmt:message key="label.menu.volumes" />',
|
||||
'label.menu.sshkeypair': '<fmt:message key="label.menu.sshkeypair" />',
|
||||
'label.metrics': '<fmt:message key="label.metrics" />',
|
||||
'label.metrics.allocated': '<fmt:message key="label.metrics.allocated" />',
|
||||
'label.metrics.clusters': '<fmt:message key="label.metrics.clusters" />',
|
||||
'label.metrics.cpu.allocated': '<fmt:message key="label.metrics.cpu.allocated" />',
|
||||
'label.metrics.cpu.max.dev': '<fmt:message key="label.metrics.cpu.max.dev" />',
|
||||
'label.metrics.cpu.total': '<fmt:message key="label.metrics.cpu.total" />',
|
||||
'label.metrics.cpu.usage': '<fmt:message key="label.metrics.cpu.usage" />',
|
||||
'label.metrics.cpu.used.avg': '<fmt:message key="label.metrics.cpu.used.avg" />',
|
||||
'label.metrics.disk': '<fmt:message key="label.metrics.disk" />',
|
||||
'label.metrics.disk.iops.total': '<fmt:message key="label.metrics.disk.iops.total" />',
|
||||
'label.metrics.disk.read': '<fmt:message key="label.metrics.disk.read" />',
|
||||
'label.metrics.disk.size': '<fmt:message key="label.metrics.disk.size" />',
|
||||
'label.metrics.disk.storagetype': '<fmt:message key="label.metrics.disk.storagetype" />',
|
||||
'label.metrics.disk.usage': '<fmt:message key="label.metrics.disk.usage" />',
|
||||
'label.metrics.disk.used': '<fmt:message key="label.metrics.disk.used" />',
|
||||
'label.metrics.disk.total': '<fmt:message key="label.metrics.disk.total" />',
|
||||
'label.metrics.disk.allocated': '<fmt:message key="label.metrics.disk.allocated" />',
|
||||
'label.metrics.disk.unallocated': '<fmt:message key="label.metrics.disk.unallocated" />',
|
||||
'label.metrics.disk.write': '<fmt:message key="label.metrics.disk.write" />',
|
||||
'label.metrics.hosts': '<fmt:message key="label.metrics.hosts" />',
|
||||
'label.metrics.memory.allocated': '<fmt:message key="label.metrics.memory.allocated" />',
|
||||
'label.metrics.memory.max.dev': '<fmt:message key="label.metrics.memory.max.dev" />',
|
||||
'label.metrics.memory.total': '<fmt:message key="label.metrics.memory.total" />',
|
||||
'label.metrics.memory.usage': '<fmt:message key="label.metrics.memory.usage" />',
|
||||
'label.metrics.memory.used.avg': '<fmt:message key="label.metrics.memory.used.avg" />',
|
||||
'label.metrics.name': '<fmt:message key="label.metrics.name" />',
|
||||
'label.metrics.network.read': '<fmt:message key="label.metrics.network.read" />',
|
||||
'label.metrics.network.usage': '<fmt:message key="label.metrics.network.usage" />',
|
||||
'label.metrics.network.write': '<fmt:message key="label.metrics.network.write" />',
|
||||
'label.metrics.num.cpu.cores': '<fmt:message key="label.metrics.num.cpu.cores" />',
|
||||
'label.metrics.property': '<fmt:message key="label.metrics.property" />',
|
||||
'label.metrics.scope': '<fmt:message key="label.metrics.scope" />',
|
||||
'label.metrics.state': '<fmt:message key="label.metrics.state" />',
|
||||
'label.metrics.storagepool': '<fmt:message key="label.metrics.storagepool" />',
|
||||
'label.metrics.vm.name': '<fmt:message key="label.metrics.vm.name" />',
|
||||
'label.migrate.instance.to': '<fmt:message key="label.migrate.instance.to" />',
|
||||
'label.migrate.instance.to.host': '<fmt:message key="label.migrate.instance.to.host" />',
|
||||
'label.migrate.instance.to.ps': '<fmt:message key="label.migrate.instance.to.ps" />',
|
||||
|
|
|
|||
|
|
@ -1800,6 +1800,7 @@
|
|||
<script type="text/javascript" src="scripts/ui-custom/granularSettings.js"></script>
|
||||
<script type="text/javascript" src="scripts/ui-custom/zoneChart.js"></script>
|
||||
<script type="text/javascript" src="scripts/ui-custom/dashboard.js"></script>
|
||||
<script type="text/javascript" src="scripts/ui-custom/metricsView.js"></script>
|
||||
<script type="text/javascript" src="scripts/installWizard.js"></script>
|
||||
<script type="text/javascript" src="scripts/ui-custom/installWizard.js"></script>
|
||||
<script type="text/javascript" src="scripts/projects.js"></script>
|
||||
|
|
@ -1836,6 +1837,7 @@
|
|||
<script type="text/javascript" src="scripts/vm_snapshots.js"></script>
|
||||
<script type="text/javascript" src="scripts/ui-custom/projectSelect.js"></script>
|
||||
<script type="text/javascript" src="scripts/ui-custom/saml.js"></script>
|
||||
<script type="text/javascript" src="scripts/metrics.js"></script>
|
||||
|
||||
<!-- Plugin/module API -->
|
||||
<script type="text/javascript" src="scripts/ui-custom/pluginListing.js"></script>
|
||||
|
|
|
|||
|
|
@ -294,7 +294,20 @@
|
|||
poll: pollAsyncJobResult
|
||||
}
|
||||
},
|
||||
snapshot: vmSnapshotAction({ listView: true })
|
||||
snapshot: vmSnapshotAction({ listView: true }),
|
||||
viewMetrics: {
|
||||
label: 'label.metrics',
|
||||
isHeader: true,
|
||||
addRow: false,
|
||||
action: {
|
||||
custom: cloudStack.uiCustom.metricsView({resource: 'vms'})
|
||||
},
|
||||
messages: {
|
||||
notification: function (args) {
|
||||
return 'label.metrics';
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
dataProvider: function(args) {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -245,6 +245,20 @@
|
|||
}
|
||||
},
|
||||
|
||||
viewMetrics: {
|
||||
label: 'label.metrics',
|
||||
isHeader: true,
|
||||
addRow: false,
|
||||
action: {
|
||||
custom: cloudStack.uiCustom.metricsView({resource: 'volumes'})
|
||||
},
|
||||
messages: {
|
||||
notification: function (args) {
|
||||
return 'label.metrics';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
uploadVolume: {
|
||||
isHeader: true,
|
||||
label: 'label.upload',
|
||||
|
|
|
|||
|
|
@ -7709,7 +7709,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
show: cloudStack.uiCustom.physicalResources({
|
||||
physicalResourceSection: {
|
||||
sections: {
|
||||
physicalResources: {
|
||||
type: 'select',
|
||||
|
|
@ -7792,7 +7792,20 @@
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
viewMetrics: {
|
||||
label: 'label.metrics',
|
||||
isHeader: true,
|
||||
addRow: false,
|
||||
action: {
|
||||
custom: cloudStack.uiCustom.metricsView({resource: 'zones'})
|
||||
},
|
||||
messages: {
|
||||
notification: function (args) {
|
||||
return 'label.metrics';
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
detailView: {
|
||||
|
|
@ -9484,7 +9497,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
},
|
||||
subsections: {
|
||||
virtualRouters: {
|
||||
sectionSelect: {
|
||||
|
|
@ -14524,6 +14537,19 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
viewMetrics: {
|
||||
label: 'label.metrics',
|
||||
isHeader: true,
|
||||
addRow: false,
|
||||
action: {
|
||||
custom: cloudStack.uiCustom.metricsView({resource: 'clusters'})
|
||||
},
|
||||
messages: {
|
||||
notification: function (args) {
|
||||
return 'label.metrics';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -15226,11 +15252,12 @@
|
|||
}
|
||||
|
||||
if (! args.context.instances) {
|
||||
array1.push("&zoneid=" + args.context.zones[0].id);
|
||||
if ("zones" in args.context)
|
||||
array1.push("&zoneid=" + args.context.zones[0].id);
|
||||
if ("pods" in args.context)
|
||||
array1.push("&podid=" + args.context.pods[0].id);
|
||||
array1.push("&podid=" + args.context.pods[0].id);
|
||||
if ("clusters" in args.context)
|
||||
array1.push("&clusterid=" + args.context.clusters[0].id);
|
||||
array1.push("&clusterid=" + args.context.clusters[0].id);
|
||||
} else {
|
||||
//Instances menu > Instance detailView > View Hosts
|
||||
array1.push("&id=" + args.context.instances[0].hostid);
|
||||
|
|
@ -15833,6 +15860,19 @@
|
|||
return 'label.add.host';
|
||||
}
|
||||
}
|
||||
},
|
||||
viewMetrics: {
|
||||
label: 'label.metrics',
|
||||
isHeader: true,
|
||||
addRow: false,
|
||||
action: {
|
||||
custom: cloudStack.uiCustom.metricsView({resource: 'hosts'})
|
||||
},
|
||||
messages: {
|
||||
notification: function (args) {
|
||||
return 'label.metrics';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
detailView: {
|
||||
|
|
@ -17647,6 +17687,19 @@
|
|||
return 'label.add.primary.storage';
|
||||
}
|
||||
}
|
||||
},
|
||||
viewMetrics: {
|
||||
label: 'label.metrics',
|
||||
isHeader: true,
|
||||
addRow: false,
|
||||
action: {
|
||||
custom: cloudStack.uiCustom.metricsView({resource: 'storagepool'})
|
||||
},
|
||||
messages: {
|
||||
notification: function (args) {
|
||||
return 'label.metrics';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -19883,6 +19936,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Inject cloudStack infra page
|
||||
cloudStack.sections.system.show = cloudStack.uiCustom.physicalResources(cloudStack.sections.system.physicalResourceSection);
|
||||
|
||||
function addExternalLoadBalancer(args, physicalNetworkObj, apiCmd, apiCmdRes, apiCmdObj) {
|
||||
var array1 =[];
|
||||
array1.push("&physicalnetworkid=" + physicalNetworkObj.id);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,140 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
(function($, cloudStack) {
|
||||
|
||||
cloudStack.uiCustom.metricsView = function(args) {
|
||||
return function() {
|
||||
var metricsListView = cloudStack.sections.metrics.listView;
|
||||
var metricsLabel = _l('label.metrics');
|
||||
|
||||
if (args.resource == 'zones') {
|
||||
metricsListView = cloudStack.sections.metrics.zones.listView;
|
||||
metricsLabel = _l('label.zones') + ' ' + metricsLabel;
|
||||
} else if (args.resource == 'clusters') {
|
||||
metricsListView = cloudStack.sections.metrics.clusters.listView;
|
||||
metricsLabel = _l('label.clusters') + ' ' + metricsLabel;
|
||||
} else if (args.resource == 'hosts') {
|
||||
metricsListView = cloudStack.sections.metrics.hosts.listView;
|
||||
metricsLabel = _l('label.hosts') + ' ' + metricsLabel;
|
||||
} else if (args.resource == 'storagepool') {
|
||||
metricsListView = cloudStack.sections.metrics.storagepool.listView;
|
||||
metricsLabel = _l('label.primary.storage') + ' ' + metricsLabel;
|
||||
} else if (args.resource == 'vms') {
|
||||
metricsListView = cloudStack.sections.metrics.instances.listView;
|
||||
metricsLabel = _l('label.instances') + ' ' + metricsLabel;
|
||||
} else if (args.resource == 'volumes') {
|
||||
metricsListView = cloudStack.sections.metrics.volumes.listView;
|
||||
metricsLabel = _l('label.volumes') + ' ' + metricsLabel;
|
||||
}
|
||||
|
||||
// list view refresh button
|
||||
metricsListView.actions = {
|
||||
refreshMetrics: {
|
||||
label: 'label.refresh',
|
||||
isHeader: true,
|
||||
addRow: true,
|
||||
action: {
|
||||
custom: function (args) {
|
||||
return function() {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
metricsListView.hideSearchBar = true;
|
||||
metricsListView.needsRefresh = true;
|
||||
metricsListView.noSplit = true;
|
||||
metricsListView.horizontalOverflow = true;
|
||||
metricsListView.groupableColumns = true;
|
||||
|
||||
if (args.resource == 'volumes') {
|
||||
metricsListView.groupableColumns = false;
|
||||
}
|
||||
|
||||
var metricsContext = cloudStack.context;
|
||||
if (metricsContext.metricsFilterData) {
|
||||
delete metricsContext.metricsFilterData;
|
||||
}
|
||||
if (args.filterBy) {
|
||||
metricsContext.metricsFilterData = {
|
||||
key: args.filterBy,
|
||||
value: args.id
|
||||
};
|
||||
}
|
||||
|
||||
var $browser = $('#browser .container');
|
||||
return $browser.cloudBrowser('addPanel', {
|
||||
title: metricsLabel,
|
||||
maximizeIfSelected: true,
|
||||
complete: function($newPanel) {
|
||||
$newPanel.listView({
|
||||
$browser: $browser,
|
||||
context: metricsContext,
|
||||
listView: metricsListView
|
||||
});
|
||||
// Make metrics tables horizontally scrollable
|
||||
$newPanel.find('.list-view').css({'overflow-x': 'visible'});
|
||||
// Refresh metrics when refresh button is clicked
|
||||
$newPanel.find('.refreshMetrics').click(function() {
|
||||
var sortedTh = $newPanel.find('table thead tr:last th.sorted');
|
||||
var thIndex = sortedTh.index();
|
||||
var thClassName = null;
|
||||
var wasSorted = false;
|
||||
var sortClassName = 'asc';
|
||||
if (sortedTh && sortedTh.hasClass('sorted')) {
|
||||
wasSorted = true;
|
||||
var classes = sortedTh.attr('class').split(/\s+/);
|
||||
thClassName = classes[0];
|
||||
if (classes.indexOf('desc') > -1) {
|
||||
sortClassName = 'desc';
|
||||
}
|
||||
}
|
||||
$browser.cloudBrowser('removeLastPanel', {});
|
||||
var refreshedPanel = cloudStack.uiCustom.metricsView(args)();
|
||||
if (wasSorted && thClassName) {
|
||||
refreshedPanel.find('th.' + thClassName).filter(function() {
|
||||
return $(this).index() == thIndex;
|
||||
}).addClass('sorted').addClass(sortClassName);
|
||||
}
|
||||
});
|
||||
|
||||
var filterMetricView = metricsListView.browseBy;
|
||||
if (filterMetricView) {
|
||||
$newPanel.bind('click', function(event) {
|
||||
event.stopPropagation();
|
||||
var $target = $(event.target);
|
||||
var id = $target.closest('tr').data('list-view-item-id');
|
||||
var jsonObj = $target.closest('tr').data('jsonObj');
|
||||
if (filterMetricView.filterKey && jsonObj) {
|
||||
if (jsonObj.hasOwnProperty(filterMetricView.filterKey)) {
|
||||
id = jsonObj[filterMetricView.filterKey];
|
||||
} else {
|
||||
return; // return if provided key is missing
|
||||
}
|
||||
}
|
||||
if (id && ($target.hasClass('first') || $target.parent().hasClass('first')) && ($target.is('td') || $target.parent().is('td'))) {
|
||||
filterMetricView.id = id;
|
||||
cloudStack.uiCustom.metricsView(filterMetricView)();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
})(jQuery, cloudStack);
|
||||
|
|
@ -177,14 +177,19 @@
|
|||
var sortData = [];
|
||||
var numericDataCount = 0;
|
||||
$elems.each(function() {
|
||||
var text = $(this).html();
|
||||
var text = $(this);
|
||||
if (hasAllRowsSameValue) {
|
||||
if (firstElem !== text) {
|
||||
if (firstElem !== text.html()) {
|
||||
hasAllRowsSameValue = false;
|
||||
}
|
||||
}
|
||||
if (text.children()) {
|
||||
text = text.children().html();
|
||||
} else {
|
||||
text = text.html();
|
||||
}
|
||||
if (isNumeric(text) || !text) {
|
||||
numericDataCount++;
|
||||
numericDataCount += 1;
|
||||
}
|
||||
sortData.push($(this));
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue