Merge "Add Metadata page to angular Launch Instance wizard"
This commit is contained in:
commit
2b0747ca11
@ -113,7 +113,8 @@
|
||||
metadataDefs: {
|
||||
flavor: null,
|
||||
image: null,
|
||||
volume: null
|
||||
volume: null,
|
||||
instance: null
|
||||
},
|
||||
networks: [],
|
||||
neutronEnabled: false,
|
||||
@ -123,6 +124,7 @@
|
||||
volumeBootable: false,
|
||||
volumes: [],
|
||||
volumeSnapshots: [],
|
||||
metadataTree: null,
|
||||
|
||||
/**
|
||||
* api methods for UI controllers
|
||||
@ -244,6 +246,7 @@
|
||||
setFinalSpecNetworks(finalSpec);
|
||||
setFinalSpecKeyPairs(finalSpec);
|
||||
setFinalSpecSecurityGroups(finalSpec);
|
||||
setFinalSpecMetadata(finalSpec);
|
||||
|
||||
return novaAPI.createServer(finalSpec).then(successMessage);
|
||||
}
|
||||
@ -513,13 +516,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
|
||||
@ -528,7 +543,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);
|
||||
|
@ -215,7 +215,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() {
|
||||
@ -230,6 +231,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({});
|
||||
});
|
||||
@ -386,6 +391,7 @@
|
||||
});
|
||||
|
||||
describe('Create Instance', function() {
|
||||
var metadata;
|
||||
|
||||
beforeEach(function() {
|
||||
// initialize some data
|
||||
@ -400,6 +406,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() {
|
||||
@ -523,6 +536,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…
x
Reference in New Issue
Block a user