Add charts to show volume quotas on Angular launch instance modal
Added two charts for Number of Volumes and Total Volume Storage quotas on Angular launch instance modal when cinder is enabled. The charts reflect the volume usage of the new instances to be created as the user changes the configuration on the modal. Updated the chart styling for the charts to align better. Change-Id: Ie744ada2317624153fcfdf9abdf4d7b26996a35e Partially-implements: blueprint launch-instance-volume-quotas
This commit is contained in:
parent
c01c4d9873
commit
336d0a0525
@ -170,26 +170,30 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getChartLabel(type, total, unit) {
|
||||||
|
if (unit) {
|
||||||
|
var totalWithUnit = total + " " + unit;
|
||||||
|
}
|
||||||
|
return interpolate(
|
||||||
|
gettext('%(total)s %(type)s'),
|
||||||
|
{ total: totalWithUnit || total,
|
||||||
|
type: type },
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// set labels depending on whether this is a max or total chart
|
// set labels depending on whether this is a max or total chart
|
||||||
if (!showChart) {
|
if (!showChart) {
|
||||||
scope.model.total = null;
|
scope.model.total = null;
|
||||||
scope.model.totalLabel = gettext('No Limit');
|
scope.model.totalLabel = gettext('No Limit');
|
||||||
} else if (angular.isDefined(scope.chartData.maxLimit)) {
|
} else if (angular.isDefined(scope.chartData.maxLimit)) {
|
||||||
scope.model.total = scope.chartData.maxLimit;
|
scope.model.total = scope.chartData.maxLimit;
|
||||||
scope.model.totalLabel = interpolate(
|
scope.model.totalLabel = getChartLabel("Max", scope.model.total, scope.chartData.unit);
|
||||||
gettext('%(total)s Max'),
|
|
||||||
{ total: scope.model.total },
|
|
||||||
true
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
scope.model.total = d3.sum(scope.chartData.data, function (d) {
|
scope.model.total = d3.sum(scope.chartData.data, function (d) {
|
||||||
return d.value;
|
return d.value;
|
||||||
});
|
});
|
||||||
scope.model.totalLabel = interpolate(
|
scope.model.totalLabel = getChartLabel("Total", scope.model.total, scope.chartData.unit);
|
||||||
gettext('%(total)s Total'),
|
|
||||||
{ total: scope.model.total },
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
scope.model.tooltipData.enabled = false;
|
scope.model.tooltipData.enabled = false;
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<chart-tooltip tooltip-data="model.tooltipData"></chart-tooltip>
|
<chart-tooltip tooltip-data="model.tooltipData"></chart-tooltip>
|
||||||
|
|
||||||
<div class="pie-chart-title" ng-if="::model.settings.showTitle && chartData.title">
|
<div class="pie-chart-title" ng-if="::model.settings.showTitle && chartData.title">
|
||||||
{$ ::chartData.title $} ({$ model.totalLabel $})
|
{$ ::chartData.title $} <br/>({$ model.totalLabel $})
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<svg class="svg-pie-chart"
|
<svg class="svg-pie-chart"
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
.pie-chart-label {
|
.pie-chart-label {
|
||||||
font-size: $font-size-large;
|
font-size: $font-size-large;
|
||||||
text-anchor: middle;
|
text-anchor: middle;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
text {
|
text {
|
||||||
font-size: $font-size-large;
|
font-size: $font-size-large;
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
describe('pie chart directive', function () {
|
describe('pie chart directive', function () {
|
||||||
|
|
||||||
var $scope, $elementMax, $elementTotal, $elementOverMax,
|
var $scope, $elementMax, $elementTotal, $elementOverMax, $elementMaxWithUnit,
|
||||||
$elementNoQuota, quotaChartDefaults;
|
$elementNoQuota, quotaChartDefaults;
|
||||||
|
|
||||||
beforeEach(module('templates'));
|
beforeEach(module('templates'));
|
||||||
@ -57,6 +57,24 @@
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.testDataMaxWithUnit = {
|
||||||
|
title: 'Total Volume Storage',
|
||||||
|
maxLimit: 1000,
|
||||||
|
unit: "GiB",
|
||||||
|
data: [
|
||||||
|
{ label: quotaChartDefaults.usageLabel,
|
||||||
|
value: 50,
|
||||||
|
colorClass: quotaChartDefaults.usageColorClass },
|
||||||
|
{ label: quotaChartDefaults.addedLabel,
|
||||||
|
value: 10,
|
||||||
|
colorClass: quotaChartDefaults.addedColorClass },
|
||||||
|
{ label: quotaChartDefaults.remainingLabel,
|
||||||
|
value: 940,
|
||||||
|
colorClass: quotaChartDefaults.remainingColorClass,
|
||||||
|
hideKey: true }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
$scope.testDataMax = {};
|
$scope.testDataMax = {};
|
||||||
$scope.testDataOverMax = {};
|
$scope.testDataOverMax = {};
|
||||||
$scope.testDataNoQuota = {};
|
$scope.testDataNoQuota = {};
|
||||||
@ -111,6 +129,12 @@
|
|||||||
$elementNoQuota = angular.element(markupNoQuota);
|
$elementNoQuota = angular.element(markupNoQuota);
|
||||||
$compile($elementNoQuota)($scope);
|
$compile($elementNoQuota)($scope);
|
||||||
|
|
||||||
|
// Max chart with unit markup
|
||||||
|
var markupMaxWithUnit = '<pie-chart chart-data="testDataMaxWithUnit" ' +
|
||||||
|
' chart-settings="chartSettings">' +
|
||||||
|
'</pie-chart>';
|
||||||
|
$elementMaxWithUnit = angular.element(markupMaxWithUnit);
|
||||||
|
$compile($elementMaxWithUnit)($scope);
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -126,6 +150,10 @@
|
|||||||
expect($elementTotal.html().trim()).not.toBe('');
|
expect($elementTotal.html().trim()).not.toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Max chart with unit should be compiled', function () {
|
||||||
|
expect($elementMaxWithUnit.html().trim()).not.toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
it('Max chart should have svg element', function () {
|
it('Max chart should have svg element', function () {
|
||||||
expect($elementMax.find('svg').length).toBe(1);
|
expect($elementMax.find('svg').length).toBe(1);
|
||||||
});
|
});
|
||||||
@ -255,6 +283,11 @@
|
|||||||
expect(cleanSpaces(firstKeyLabel.textContent)).toEqual('1 Current Usage');
|
expect(cleanSpaces(firstKeyLabel.textContent)).toEqual('1 Current Usage');
|
||||||
expect(cleanSpaces(secondKeyLabel.textContent)).toEqual('1 Added');
|
expect(cleanSpaces(secondKeyLabel.textContent)).toEqual('1 Added');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Max chart with unit should have the unit in its title', function () {
|
||||||
|
var title = $elementMaxWithUnit.find('.pie-chart-title').text().trim();
|
||||||
|
expect(title).toBe('Total Volume Storage (1000 GiB Max)');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -40,6 +40,8 @@
|
|||||||
ctrl.chartTotalInstancesLabel = gettext('Total Instances');
|
ctrl.chartTotalInstancesLabel = gettext('Total Instances');
|
||||||
ctrl.chartTotalVcpusLabel = gettext('Total VCPUs');
|
ctrl.chartTotalVcpusLabel = gettext('Total VCPUs');
|
||||||
ctrl.chartTotalRamLabel = gettext('Total RAM');
|
ctrl.chartTotalRamLabel = gettext('Total RAM');
|
||||||
|
ctrl.chartTotalVolumeLabel = gettext('Total Volumes');
|
||||||
|
ctrl.chartTotalVolumeStorageLabel = gettext('Total Volume Storage');
|
||||||
|
|
||||||
ctrl.filterFacets = [
|
ctrl.filterFacets = [
|
||||||
{
|
{
|
||||||
@ -160,6 +162,25 @@
|
|||||||
ctrl.validateFlavor();
|
ctrl.validateFlavor();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var cinderLimitsWatcher = $scope.$watch(function () {
|
||||||
|
return launchInstanceModel.cinderLimits;
|
||||||
|
}, function (newValue, oldValue, scope) {
|
||||||
|
var ctrl = scope.selectFlavorCtrl;
|
||||||
|
ctrl.cinderLimits = newValue;
|
||||||
|
ctrl.updateFlavorFacades();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
var volumeSizeWatcher = $scope.$watchCollection(function () {
|
||||||
|
return [launchInstanceModel.newInstanceSpec.source_type,
|
||||||
|
launchInstanceModel.newInstanceSpec.vol_size,
|
||||||
|
launchInstanceModel.newInstanceSpec.vol_create];
|
||||||
|
}, function (newValue, oldValue) {
|
||||||
|
if (!angular.equals(newValue, oldValue)) {
|
||||||
|
ctrl.updateFlavorFacades();
|
||||||
|
ctrl.validateFlavor();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
$scope.$on('$destroy', function() {
|
$scope.$on('$destroy', function() {
|
||||||
novaLimitsWatcher();
|
novaLimitsWatcher();
|
||||||
@ -167,6 +188,8 @@
|
|||||||
instanceCountWatcher();
|
instanceCountWatcher();
|
||||||
facadesWatcher();
|
facadesWatcher();
|
||||||
sourceWatcher();
|
sourceWatcher();
|
||||||
|
cinderLimitsWatcher();
|
||||||
|
volumeSizeWatcher();
|
||||||
});
|
});
|
||||||
|
|
||||||
//////////
|
//////////
|
||||||
@ -240,6 +263,7 @@
|
|||||||
*/
|
*/
|
||||||
for (var i = 0; i < ctrl.availableFlavorFacades.length; i++) {
|
for (var i = 0; i < ctrl.availableFlavorFacades.length; i++) {
|
||||||
var facade = ctrl.availableFlavorFacades[i];
|
var facade = ctrl.availableFlavorFacades[i];
|
||||||
|
var createVolume = launchInstanceModel.newInstanceSpec.vol_create;
|
||||||
|
|
||||||
facade.instancesChartData = instancesChartData;
|
facade.instancesChartData = instancesChartData;
|
||||||
|
|
||||||
@ -253,7 +277,27 @@
|
|||||||
ctrl.chartTotalRamLabel,
|
ctrl.chartTotalRamLabel,
|
||||||
ctrl.instanceCount * facade.ram,
|
ctrl.instanceCount * facade.ram,
|
||||||
launchInstanceModel.novaLimits.totalRAMUsed,
|
launchInstanceModel.novaLimits.totalRAMUsed,
|
||||||
launchInstanceModel.novaLimits.maxTotalRAMSize);
|
launchInstanceModel.novaLimits.maxTotalRAMSize,
|
||||||
|
"MB"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (launchInstanceModel.cinderLimits) {
|
||||||
|
facade.volumeChartData = ctrl.getChartData(
|
||||||
|
ctrl.chartTotalVolumeLabel,
|
||||||
|
createVolume ? ctrl.instanceCount : 0,
|
||||||
|
launchInstanceModel.cinderLimits.totalVolumesUsed,
|
||||||
|
launchInstanceModel.cinderLimits.maxTotalVolumes
|
||||||
|
);
|
||||||
|
|
||||||
|
facade.volumeStorageChartData = ctrl.getChartData(
|
||||||
|
ctrl.chartTotalVolumeStorageLabel,
|
||||||
|
createVolume ? (ctrl.instanceCount * Math.max(facade.totalDisk,
|
||||||
|
launchInstanceModel.newInstanceSpec.vol_size)) : 0,
|
||||||
|
launchInstanceModel.cinderLimits.totalGigabytesUsed,
|
||||||
|
launchInstanceModel.cinderLimits.maxTotalVolumeGigabytes,
|
||||||
|
"GiB"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
var errors = ctrl.getErrors(facade.flavor);
|
var errors = ctrl.getErrors(facade.flavor);
|
||||||
facade.errors = errors;
|
facade.errors = errors;
|
||||||
@ -261,7 +305,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getChartData(title, added, totalUsed, maxAllowed) {
|
function getChartData(title, added, totalUsed, maxAllowed, unit) {
|
||||||
|
|
||||||
var used = ctrl.defaultIfUndefined(totalUsed, 0);
|
var used = ctrl.defaultIfUndefined(totalUsed, 0);
|
||||||
var allowed = ctrl.defaultIfUndefined(maxAllowed, 1);
|
var allowed = ctrl.defaultIfUndefined(maxAllowed, 1);
|
||||||
@ -288,7 +332,8 @@
|
|||||||
maxLimit: allowed,
|
maxLimit: allowed,
|
||||||
label: quotaCalc + '%',
|
label: quotaCalc + '%',
|
||||||
overMax: overMax,
|
overMax: overMax,
|
||||||
data: [usageData, addedData, remainingData]
|
data: [usageData, addedData, remainingData],
|
||||||
|
unit: unit
|
||||||
};
|
};
|
||||||
|
|
||||||
return chartData;
|
return chartData;
|
||||||
|
@ -113,6 +113,16 @@ limitations under the License.
|
|||||||
chart-settings="chartSettings"></pie-chart>
|
chart-settings="chartSettings"></pie-chart>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row" ng-if="selectFlavorCtrl.cinderLimits">
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<pie-chart chart-data="item.volumeChartData"
|
||||||
|
chart-settings="chartSettings"></pie-chart>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<pie-chart chart-data="item.volumeStorageChartData"
|
||||||
|
chart-settings="chartSettings"></pie-chart>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div ng-if="selectFlavorCtrl.metadataDefs.flavor">
|
<div ng-if="selectFlavorCtrl.metadataDefs.flavor">
|
||||||
<div class="row" ng-if="item.extras">
|
<div class="row" ng-if="item.extras">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
@ -224,6 +234,16 @@ limitations under the License.
|
|||||||
chart-settings="chartSettings"></pie-chart>
|
chart-settings="chartSettings"></pie-chart>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row" ng-if="selectFlavorCtrl.cinderLimits">
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<pie-chart chart-data="item.volumeChartData"
|
||||||
|
chart-settings="chartSettings"></pie-chart>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4">
|
||||||
|
<pie-chart chart-data="item.volumeStorageChartData"
|
||||||
|
chart-settings="chartSettings"></pie-chart>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div ng-if="selectFlavorCtrl.metadataDefs.flavor">
|
<div ng-if="selectFlavorCtrl.metadataDefs.flavor">
|
||||||
<div class="row" ng-if="item.extras">
|
<div class="row" ng-if="item.extras">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
model = { newInstanceSpec: { },
|
model = { newInstanceSpec: { },
|
||||||
novaLimits: { },
|
novaLimits: { },
|
||||||
|
cinderLimits: { },
|
||||||
flavors: []
|
flavors: []
|
||||||
};
|
};
|
||||||
defaults = { usageLabel: "label",
|
defaults = { usageLabel: "label",
|
||||||
@ -54,7 +55,10 @@
|
|||||||
it('defines expected labels', function () {
|
it('defines expected labels', function () {
|
||||||
var props = [
|
var props = [
|
||||||
'chartTotalInstancesLabel',
|
'chartTotalInstancesLabel',
|
||||||
'chartTotalVcpusLabel', 'chartTotalRamLabel'
|
'chartTotalVcpusLabel',
|
||||||
|
'chartTotalRamLabel',
|
||||||
|
'chartTotalVolumeLabel',
|
||||||
|
'chartTotalVolumeStorageLabel'
|
||||||
];
|
];
|
||||||
|
|
||||||
props.forEach(function (prop) {
|
props.forEach(function (prop) {
|
||||||
@ -75,6 +79,11 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('includes the unit if it is provided', function () {
|
||||||
|
var data = ctrl.getChartData('fakeTitle', 1, 2, 3, "MB");
|
||||||
|
expect(data.unit).toBe('MB');
|
||||||
|
});
|
||||||
|
|
||||||
describe("watches", function () {
|
describe("watches", function () {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
@ -93,14 +102,14 @@
|
|||||||
ctrl.validateFlavor.calls.reset();
|
ctrl.validateFlavor.calls.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("establishes five watches", function () {
|
it("establishes seven watches", function () {
|
||||||
// Count calls to $watch (note: $watchCollection
|
// Count calls to $watch (note: $watchCollection
|
||||||
// also calls $watch)
|
// also calls $watch)
|
||||||
expect(scope.$watch.calls.count()).toBe(5);
|
expect(scope.$watch.calls.count()).toBe(7);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("establishes three watch collections", function () {
|
it("establishes four watch collections", function () {
|
||||||
expect(scope.$watchCollection.calls.count()).toBe(3);
|
expect(scope.$watchCollection.calls.count()).toBe(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("novaLimits watch", function () {
|
describe("novaLimits watch", function () {
|
||||||
@ -245,6 +254,40 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("cinderLimits watcher", function () {
|
||||||
|
|
||||||
|
it("initializes cinderLimits", function () {
|
||||||
|
expect(ctrl.cinderLimits).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call updateFlavorFacades", function () {
|
||||||
|
model.cinderLimits = {test: "test"};
|
||||||
|
scope.$apply();
|
||||||
|
expect(ctrl.cinderLimits).toEqual({test: "test"});
|
||||||
|
expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("volume size watcher", function () {
|
||||||
|
|
||||||
|
it("should call updateFlavorFacades when source type is changed", function () {
|
||||||
|
model.newInstanceSpec.source_type = "image";
|
||||||
|
scope.$apply();
|
||||||
|
expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call updateFlavorFacades when volume size is changed", function () {
|
||||||
|
model.newInstanceSpec.vol_size = 10;
|
||||||
|
scope.$apply();
|
||||||
|
expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call updateFlavorFacades when volume create is changed", function () {
|
||||||
|
model.newInstanceSpec.vol_create = true;
|
||||||
|
scope.$apply();
|
||||||
|
expect(ctrl.updateFlavorFacades.calls.count()).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when having allocated flavors", function () {
|
describe("when having allocated flavors", function () {
|
||||||
@ -412,6 +455,36 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("test updateFlavorFacades", function () {
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
ctrl.flavors = [{name: "tiny"}];
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set volumeChartData and volumeStorageChartData", function () {
|
||||||
|
ctrl.updateFlavorFacades();
|
||||||
|
expect(ctrl.availableFlavorFacades.length).toBe(1);
|
||||||
|
expect(ctrl.availableFlavorFacades[0].volumeChartData).toBeDefined();
|
||||||
|
expect(ctrl.availableFlavorFacades[0].volumeStorageChartData).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call getChartData", function() {
|
||||||
|
spyOn(ctrl, 'getChartData');
|
||||||
|
ctrl.updateFlavorFacades();
|
||||||
|
expect(ctrl.getChartData.calls.count()).toBe(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("test validateFlavor", function () {
|
||||||
|
|
||||||
|
it("should call validateFlavor when source type is changed", function () {
|
||||||
|
spyOn(ctrl, 'validateFlavor');
|
||||||
|
model.newInstanceSpec.source_type = "image";
|
||||||
|
scope.$apply();
|
||||||
|
expect(ctrl.validateFlavor.calls.count()).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -561,6 +561,7 @@
|
|||||||
function addVolumeSourcesIfEnabled(config) {
|
function addVolumeSourcesIfEnabled(config) {
|
||||||
var volumeDeferred = $q.defer();
|
var volumeDeferred = $q.defer();
|
||||||
var volumeSnapshotDeferred = $q.defer();
|
var volumeSnapshotDeferred = $q.defer();
|
||||||
|
var absoluteLimitsDeferred = $q.defer();
|
||||||
serviceCatalog
|
serviceCatalog
|
||||||
.ifTypeEnabled('volumev2')
|
.ifTypeEnabled('volumev2')
|
||||||
.then(onVolumeServiceEnabled, onCheckVolumeV3);
|
.then(onVolumeServiceEnabled, onCheckVolumeV3);
|
||||||
@ -576,8 +577,10 @@
|
|||||||
.then(onBootToVolumeSupported);
|
.then(onBootToVolumeSupported);
|
||||||
if (!config || !config.disable_volume) {
|
if (!config || !config.disable_volume) {
|
||||||
getVolumes().then(resolveVolumes, failVolumes);
|
getVolumes().then(resolveVolumes, failVolumes);
|
||||||
|
getAbsoluteLimits().then(resolveAbsoluteLimitsDeferred, resolveAbsoluteLimitsDeferred);
|
||||||
} else {
|
} else {
|
||||||
resolveVolumes();
|
resolveVolumes();
|
||||||
|
resolveAbsoluteLimitsDeferred();
|
||||||
}
|
}
|
||||||
if (!config || !config.disable_volume_snapshot) {
|
if (!config || !config.disable_volume_snapshot) {
|
||||||
getVolumeSnapshots().then(resolveVolumeSnapshots, failVolumeSnapshots);
|
getVolumeSnapshots().then(resolveVolumeSnapshots, failVolumeSnapshots);
|
||||||
@ -592,6 +595,9 @@
|
|||||||
return cinderAPI.getVolumes({status: 'available', bootable: 1})
|
return cinderAPI.getVolumes({status: 'available', bootable: 1})
|
||||||
.then(onGetVolumes);
|
.then(onGetVolumes);
|
||||||
}
|
}
|
||||||
|
function getAbsoluteLimits() {
|
||||||
|
return cinderAPI.getAbsoluteLimits().then(onGetCinderLimits);
|
||||||
|
}
|
||||||
function getVolumeSnapshots() {
|
function getVolumeSnapshots() {
|
||||||
return cinderAPI.getVolumeSnapshots({status: 'available'})
|
return cinderAPI.getVolumeSnapshots({status: 'available'})
|
||||||
.then(onGetVolumeSnapshots);
|
.then(onGetVolumeSnapshots);
|
||||||
@ -599,6 +605,7 @@
|
|||||||
function resolvePromises() {
|
function resolvePromises() {
|
||||||
volumeDeferred.resolve();
|
volumeDeferred.resolve();
|
||||||
volumeSnapshotDeferred.resolve();
|
volumeSnapshotDeferred.resolve();
|
||||||
|
absoluteLimitsDeferred.resolve();
|
||||||
}
|
}
|
||||||
function resolveVolumes() {
|
function resolveVolumes() {
|
||||||
volumeDeferred.resolve();
|
volumeDeferred.resolve();
|
||||||
@ -612,10 +619,14 @@
|
|||||||
function failVolumeSnapshots() {
|
function failVolumeSnapshots() {
|
||||||
volumeSnapshotDeferred.resolve();
|
volumeSnapshotDeferred.resolve();
|
||||||
}
|
}
|
||||||
|
function resolveAbsoluteLimitsDeferred() {
|
||||||
|
absoluteLimitsDeferred.resolve();
|
||||||
|
}
|
||||||
return $q.all(
|
return $q.all(
|
||||||
[
|
[
|
||||||
volumeDeferred.promise,
|
volumeDeferred.promise,
|
||||||
volumeSnapshotDeferred.promise
|
volumeSnapshotDeferred.promise,
|
||||||
|
absoluteLimitsDeferred.promise
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,6 +754,12 @@
|
|||||||
finalSpec.source_id = '';
|
finalSpec.source_id = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cinder Limits
|
||||||
|
|
||||||
|
function onGetCinderLimits(response) {
|
||||||
|
model.cinderLimits = response.data;
|
||||||
|
}
|
||||||
|
|
||||||
// Nova Limits
|
// Nova Limits
|
||||||
|
|
||||||
function onGetNovaLimits(data) {
|
function onGetNovaLimits(data) {
|
||||||
|
@ -232,6 +232,15 @@
|
|||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
deferred.resolve({ data: { items: snapshots } });
|
deferred.resolve({ data: { items: snapshots } });
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
},
|
||||||
|
getAbsoluteLimits: function() {
|
||||||
|
var limits = { maxTotalVolumes: 100,
|
||||||
|
totalVolumesUsed: 2,
|
||||||
|
maxTotalVolumeGigabytes: 1000,
|
||||||
|
totalGigabytesUsed: 10 };
|
||||||
|
var deferred = $q.defer();
|
||||||
|
deferred.resolve({ data: limits });
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -732,6 +741,16 @@
|
|||||||
expect(model.allowedBootSources).toContain(VOLUME_SNAPSHOT);
|
expect(model.allowedBootSources).toContain(VOLUME_SNAPSHOT);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should have maxTotalVolumes and maxTotalVolumeGigabytes if cinder ' +
|
||||||
|
'is enabled', function() {
|
||||||
|
cinderEnabled = true;
|
||||||
|
model.initialize(true);
|
||||||
|
scope.$apply();
|
||||||
|
|
||||||
|
expect(model.cinderLimits.maxTotalVolumes).toBe(100);
|
||||||
|
expect(model.cinderLimits.maxTotalVolumeGigabytes).toBe(1000);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Post Initialization Model - Initializing', function() {
|
describe('Post Initialization Model - Initializing', function() {
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
|
|
||||||
.transfer-section {
|
.transfer-section {
|
||||||
margin-top: $padding-large-vertical;
|
margin-top: $padding-large-vertical;
|
||||||
|
|
||||||
|
.row .pie-chart {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.magic-search-bar, .basic-search-bar {
|
.magic-search-bar, .basic-search-bar {
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Added two charts to show the Number of Volumes and Total Volume Storage
|
||||||
|
quotas on launch instance modal when cinder is enabled.
|
Loading…
x
Reference in New Issue
Block a user