Add Metadata page to angular Launch Instance wizard
This adds the Metadata page to the angular Launch Instance wizard and allows users to add metadata to an instance when launching. Limits will only be enforced once this patch merges: https://review.openstack.org/209680 Implements: blueprint add-server-metadata Change-Id: If4202e2e0c934d969ebfb1a566cb04848ad1c28e
This commit is contained in:
parent
1178757445
commit
98c4d0548f
@ -115,7 +115,8 @@
|
||||
metadataDefs: {
|
||||
flavor: null,
|
||||
image: null,
|
||||
volume: null
|
||||
volume: null,
|
||||
instance: null
|
||||
},
|
||||
networks: [],
|
||||
neutronEnabled: false,
|
||||
@ -125,6 +126,7 @@
|
||||
volumeBootable: false,
|
||||
volumes: [],
|
||||
volumeSnapshots: [],
|
||||
metadataTree: null,
|
||||
|
||||
/**
|
||||
* api methods for UI controllers
|
||||
@ -246,6 +248,7 @@
|
||||
setFinalSpecNetworks(finalSpec);
|
||||
setFinalSpecKeyPairs(finalSpec);
|
||||
setFinalSpecSecurityGroups(finalSpec);
|
||||
setFinalSpecMetadata(finalSpec);
|
||||
|
||||
return novaAPI.createServer(finalSpec).then(successMessage);
|
||||
}
|
||||
@ -515,13 +518,25 @@
|
||||
angular.extend(model.novaLimits, data.data);
|
||||
}
|
||||
|
||||
// Instance metadata
|
||||
|
||||
function setFinalSpecMetadata(finalSpec) {
|
||||
if (model.metadataTree) {
|
||||
var meta = model.metadataTree.getExisting();
|
||||
if (!angular.equals({}, meta)) {
|
||||
angular.forEach(meta, function(value, key) {
|
||||
meta[key] = value + '';
|
||||
});
|
||||
finalSpec.meta = meta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Metadata Definitions
|
||||
|
||||
/**
|
||||
* Metadata definitions provide supplemental information in detail
|
||||
* rows and should not slow down any of the other load processes.
|
||||
* All code should be written to treat metadata definitions as
|
||||
* optional, because they are never guaranteed to exist.
|
||||
* Metadata definitions provide supplemental information in source image detail
|
||||
* rows and are used on the metadata tab for adding metadata to the instance.
|
||||
*/
|
||||
function getMetadataDefinitions() {
|
||||
// Metadata definitions often apply to multiple
|
||||
@ -530,7 +545,8 @@
|
||||
var resourceTypes = {
|
||||
flavor: 'OS::Nova::Flavor',
|
||||
image: 'OS::Glance::Image',
|
||||
volume: 'OS::Cinder::Volumes'
|
||||
volume: 'OS::Cinder::Volumes',
|
||||
instance: 'OS::Nova::Instance'
|
||||
};
|
||||
|
||||
angular.forEach(resourceTypes, applyForResourceType);
|
||||
|
@ -217,7 +217,8 @@
|
||||
expect(model.metadataDefs.flavor).toBeNull();
|
||||
expect(model.metadataDefs.image).toBeNull();
|
||||
expect(model.metadataDefs.volume).toBeNull();
|
||||
expect(Object.keys(model.metadataDefs).length).toBe(3);
|
||||
expect(model.metadataDefs.instance).toBeNull();
|
||||
expect(Object.keys(model.metadataDefs).length).toBe(4);
|
||||
});
|
||||
|
||||
it('defaults "allow create volume from image" to false', function() {
|
||||
@ -232,6 +233,10 @@
|
||||
expect(model.volumeBootable).toBe(false);
|
||||
});
|
||||
|
||||
it('defaults "metadataTree" to null', function() {
|
||||
expect(model.metadataTree).toBe(null);
|
||||
});
|
||||
|
||||
it('initializes "nova limits" to empty object', function() {
|
||||
expect(model.novaLimits).toEqual({});
|
||||
});
|
||||
@ -388,6 +393,7 @@
|
||||
});
|
||||
|
||||
describe('Create Instance', function() {
|
||||
var metadata;
|
||||
|
||||
beforeEach(function() {
|
||||
// initialize some data
|
||||
@ -402,6 +408,13 @@
|
||||
model.newInstanceSpec.vol_delete_on_instance_delete = true;
|
||||
model.newInstanceSpec.vol_device_name = "volTestName";
|
||||
model.newInstanceSpec.vol_size = 10;
|
||||
|
||||
metadata = {'foo': 'bar'};
|
||||
model.metadataTree = {
|
||||
getExisting: function() {
|
||||
return metadata;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
it('should set final spec in format required by Nova (Neutron disabled)', function() {
|
||||
@ -525,6 +538,24 @@
|
||||
var finalSpec = model.createInstance();
|
||||
expect(finalSpec.block_device_mapping_v2[0].device_name).toBeNull();
|
||||
});
|
||||
|
||||
it('should not have meta property if no metadata specified', function() {
|
||||
metadata = {};
|
||||
|
||||
var finalSpec = model.createInstance();
|
||||
expect(finalSpec.meta).toBeUndefined();
|
||||
|
||||
model.metadataTree = null;
|
||||
|
||||
finalSpec = model.createInstance();
|
||||
expect(finalSpec.meta).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should have meta property if metadata specified', function() {
|
||||
var finalSpec = model.createInstance();
|
||||
expect(finalSpec.meta).toBe(metadata);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -79,6 +79,12 @@
|
||||
templateUrl: basePath + 'configuration/configuration.html',
|
||||
helpUrl: basePath + 'configuration/configuration.help.html',
|
||||
formName: 'launchInstanceConfigurationForm'
|
||||
},
|
||||
{
|
||||
title: gettext('Metadata'),
|
||||
templateUrl: basePath + 'metadata/metadata.html',
|
||||
helpUrl: basePath + 'metadata/metadata.help.html',
|
||||
formName: 'launchInstanceMetadataForm'
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -47,9 +47,9 @@
|
||||
expect(launchInstanceWorkflow.title).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have the seven steps defined', function () {
|
||||
it('should have the eight steps defined', function () {
|
||||
expect(launchInstanceWorkflow.steps).toBeDefined();
|
||||
expect(launchInstanceWorkflow.steps.length).toBe(7);
|
||||
expect(launchInstanceWorkflow.steps.length).toBe(8);
|
||||
|
||||
var forms = [
|
||||
'launchInstanceDetailsForm',
|
||||
@ -58,7 +58,8 @@
|
||||
'launchInstanceNetworkForm',
|
||||
'launchInstanceAccessAndSecurityForm',
|
||||
'launchInstanceKeypairForm',
|
||||
'launchInstanceConfigurationForm'
|
||||
'launchInstanceConfigurationForm',
|
||||
'launchInstanceMetadataForm'
|
||||
];
|
||||
|
||||
forms.forEach(function(expectedForm, idx) {
|
||||
|
@ -0,0 +1,17 @@
|
||||
<div>
|
||||
<h1 translate>Metadata Help</h1>
|
||||
<p translate>
|
||||
You can add arbitrary metadata to your instance so that you can more easily identify it among other running instances. Metadata is a collection of key-value pairs associated with an instance. The maximum length for each metadata key and value is 255 characters.
|
||||
</p>
|
||||
<p>
|
||||
<span translate>
|
||||
The maximum number of key-value pairs that can be supplied per instance is determined by the compute provider.
|
||||
</span>
|
||||
<span ng-if="model.novaLimits.maxServerMeta > -1" translate>
|
||||
This limit is currently set to {$ model.novaLimits.maxServerMeta $}.
|
||||
</span>
|
||||
<span ng-if="model.novaLimits.maxServerMeta <= -1" translate>
|
||||
This limit is currently not set.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
@ -0,0 +1,14 @@
|
||||
<div>
|
||||
<h1 translate>Metadata</h1>
|
||||
<div class="content">
|
||||
<metadata-tree
|
||||
ng-if="model.metadataDefs.instance && model.novaLimits"
|
||||
available="::model.metadataDefs.instance"
|
||||
existing="{}"
|
||||
max-key-length="255"
|
||||
max-value-length="255"
|
||||
max-item-count="::model.novaLimits.maxServerMeta"
|
||||
model="::model.metadataTree">
|
||||
</metadata-tree>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2015 IBM Corp.
|
||||
*
|
||||
* Licensed 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() {
|
||||
'use strict';
|
||||
|
||||
describe('Launch Instance Metadata Step', function() {
|
||||
|
||||
describe('metadata tree', function() {
|
||||
var $scope, $element, model;
|
||||
|
||||
beforeEach(module('templates'));
|
||||
beforeEach(module('horizon.dashboard.project.workflow.launch-instance'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
var $compile = $injector.get('$compile');
|
||||
var $templateCache = $injector.get('$templateCache');
|
||||
var basePath = $injector.get('horizon.dashboard.project.workflow.launch-instance.basePath');
|
||||
var markup = $templateCache.get(basePath + 'metadata/metadata.html');
|
||||
model = {
|
||||
metadataDefs: { instance: false },
|
||||
novaLimits: false
|
||||
};
|
||||
$scope = $injector.get('$rootScope').$new();
|
||||
$scope.model = model;
|
||||
$element = $compile(markup)($scope);
|
||||
}));
|
||||
|
||||
it('should create metadata tree only after dependencies are received', function() {
|
||||
expect($element.find('metadata-tree').length).toBe(0);
|
||||
|
||||
model.metadataDefs.instance = {};
|
||||
$scope.$apply();
|
||||
|
||||
expect($element.find('metadata-tree').length).toBe(0);
|
||||
|
||||
model.novaLimits = {};
|
||||
$scope.$apply();
|
||||
|
||||
expect($element.find('metadata-tree').length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('metadata step help', function() {
|
||||
var $scope, $element, model;
|
||||
|
||||
beforeEach(module('templates'));
|
||||
beforeEach(module('horizon.dashboard.project.workflow.launch-instance'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
var $compile = $injector.get('$compile');
|
||||
var $templateCache = $injector.get('$templateCache');
|
||||
var basePath = $injector.get('horizon.dashboard.project.workflow.launch-instance.basePath');
|
||||
var markup = $templateCache.get(basePath + 'metadata/metadata.help.html');
|
||||
$scope = $injector.get('$rootScope').$new();
|
||||
model = {
|
||||
novaLimits: {
|
||||
maxServerMeta: null
|
||||
}
|
||||
};
|
||||
$scope.model = model;
|
||||
$element = $compile(markup)($scope);
|
||||
}));
|
||||
|
||||
it('should update message based on nova limit', function() {
|
||||
expect($element.find('p+p>span').length).toBe(1);
|
||||
|
||||
model.novaLimits.maxServerMeta = -1;
|
||||
$scope.$apply();
|
||||
|
||||
expect($element.find('p+p>span').length).toBe(2);
|
||||
expect($element.find('p+p>span:last-child').text().trim())
|
||||
.toBe('This limit is currently not set.');
|
||||
|
||||
model.novaLimits.maxServerMeta = 5;
|
||||
$scope.$apply();
|
||||
|
||||
expect($element.find('p+p>span').length).toBe(2);
|
||||
expect($element.find('p+p>span:last-child').text().trim())
|
||||
.toMatch(/^This limit is currently set to/);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- Added the Metadata tab to the new Launch Instance workflow to allow adding
|
||||
key-value metadata to an instance at launch. This includes any properties
|
||||
from the OS::Nova::Instance namespace of the glance metadata definitions.
|
Loading…
Reference in New Issue
Block a user