Merge "Add charts to show volume quotas on Angular launch instance modal"
This commit is contained in:
commit
cbb85753fb
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -569,6 +569,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);
|
||||||
|
@ -584,8 +585,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);
|
||||||
|
@ -600,6 +603,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);
|
||||||
|
@ -607,6 +613,7 @@
|
||||||
function resolvePromises() {
|
function resolvePromises() {
|
||||||
volumeDeferred.resolve();
|
volumeDeferred.resolve();
|
||||||
volumeSnapshotDeferred.resolve();
|
volumeSnapshotDeferred.resolve();
|
||||||
|
absoluteLimitsDeferred.resolve();
|
||||||
}
|
}
|
||||||
function resolveVolumes() {
|
function resolveVolumes() {
|
||||||
volumeDeferred.resolve();
|
volumeDeferred.resolve();
|
||||||
|
@ -620,10 +627,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
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,6 +762,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) {
|
||||||
|
|
|
@ -233,6 +233,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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -751,6 +760,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…
Reference in New Issue