Allow setting default value for config_drive
This adds a setting that can be used to specify the default value for the Configuration Drive option when launching an instance. Closes-Bug: #1537106 Change-Id: If402d3331158b462bece27fa6fce2bdb7f6a4a2e
This commit is contained in:
parent
bbf163cb2b
commit
73bb9a5943
@ -508,6 +508,22 @@ This setting can be used in the case where a separate panel is used for
|
|||||||
managing a custom property or if a certain custom property should never be
|
managing a custom property or if a certain custom property should never be
|
||||||
edited.
|
edited.
|
||||||
|
|
||||||
|
``LAUNCH_INSTANCE_DEFAULTS``
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
.. versionadded:: 9.0.0(Mitaka)
|
||||||
|
|
||||||
|
Default::
|
||||||
|
|
||||||
|
{
|
||||||
|
"config_drive": False
|
||||||
|
}
|
||||||
|
|
||||||
|
A dictionary of settings which can be used to provide the default values for
|
||||||
|
properties found in the Launch Instance modal.
|
||||||
|
|
||||||
|
The ``config_drive`` setting specifies the default value for the Configuration
|
||||||
|
Drive property.
|
||||||
|
|
||||||
``MESSAGES_PATH``
|
``MESSAGES_PATH``
|
||||||
-----------------
|
-----------------
|
||||||
@ -529,7 +545,6 @@ data and must have a .json file extension. For example::
|
|||||||
|
|
||||||
Possible values for level are: success, info, warning and error.
|
Possible values for level are: success, info, warning and error.
|
||||||
|
|
||||||
|
|
||||||
``OPENSTACK_API_VERSIONS``
|
``OPENSTACK_API_VERSIONS``
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ setup.py
|
|||||||
pbr=True)
|
pbr=True)
|
||||||
|
|
||||||
setup.cfg
|
setup.cfg
|
||||||
--------
|
---------
|
||||||
::
|
::
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
|
@ -1477,6 +1477,7 @@ class InstanceTests(helpers.TestCase):
|
|||||||
only_one_network=False,
|
only_one_network=False,
|
||||||
disk_config=True,
|
disk_config=True,
|
||||||
config_drive=True,
|
config_drive=True,
|
||||||
|
config_drive_default=False,
|
||||||
test_with_profile=False):
|
test_with_profile=False):
|
||||||
image = self.images.first()
|
image = self.images.first()
|
||||||
|
|
||||||
@ -1619,6 +1620,10 @@ class InstanceTests(helpers.TestCase):
|
|||||||
else:
|
else:
|
||||||
self.assertNotContains(res, config_drive_field_label)
|
self.assertNotContains(res, config_drive_field_label)
|
||||||
|
|
||||||
|
step = workflow.get_step("setadvancedaction")
|
||||||
|
self.assertEqual(step.action.initial['config_drive'],
|
||||||
|
config_drive_default)
|
||||||
|
|
||||||
@django.test.utils.override_settings(
|
@django.test.utils.override_settings(
|
||||||
OPENSTACK_HYPERVISOR_FEATURES={'can_set_password': False})
|
OPENSTACK_HYPERVISOR_FEATURES={'can_set_password': False})
|
||||||
def test_launch_instance_get_without_password(self):
|
def test_launch_instance_get_without_password(self):
|
||||||
@ -1634,6 +1639,11 @@ class InstanceTests(helpers.TestCase):
|
|||||||
self._test_launch_form_instance_requirement_error(image, flavor,
|
self._test_launch_form_instance_requirement_error(image, flavor,
|
||||||
keypair_require=True)
|
keypair_require=True)
|
||||||
|
|
||||||
|
@django.test.utils.override_settings(
|
||||||
|
LAUNCH_INSTANCE_DEFAULTS={'config_drive': True})
|
||||||
|
def test_launch_instance_get_with_config_drive_default(self):
|
||||||
|
self.test_launch_instance_get(config_drive_default=True)
|
||||||
|
|
||||||
def test_launch_instance_get_no_block_device_mapping_v2_supported(self):
|
def test_launch_instance_get_no_block_device_mapping_v2_supported(self):
|
||||||
self.test_launch_instance_get(block_device_mapping_v2=False)
|
self.test_launch_instance_get(block_device_mapping_v2=False)
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ Views for managing instances.
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.urlresolvers import reverse_lazy
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django import http
|
from django import http
|
||||||
@ -147,6 +148,8 @@ class LaunchInstanceView(workflows.WorkflowView):
|
|||||||
initial = super(LaunchInstanceView, self).get_initial()
|
initial = super(LaunchInstanceView, self).get_initial()
|
||||||
initial['project_id'] = self.request.user.tenant_id
|
initial['project_id'] = self.request.user.tenant_id
|
||||||
initial['user_id'] = self.request.user.id
|
initial['user_id'] = self.request.user.id
|
||||||
|
defaults = getattr(settings, 'LAUNCH_INSTANCE_DEFAULTS', {})
|
||||||
|
initial['config_drive'] = defaults.get('config_drive', False)
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,18 +15,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var MAX_SCRIPT_SIZE = 16 * 1024;
|
var MAX_SCRIPT_SIZE = 16 * 1024;
|
||||||
var DEFAULT_CONFIG_DRIVE = false;
|
|
||||||
var DEFAULT_USER_DATA = '';
|
|
||||||
var DEFAULT_DISK_CONFIG = 'AUTO';
|
|
||||||
|
|
||||||
angular
|
angular
|
||||||
.module('horizon.dashboard.project.workflow.launch-instance')
|
.module('horizon.dashboard.project.workflow.launch-instance')
|
||||||
.controller('LaunchInstanceConfigurationController', LaunchInstanceConfigurationController);
|
.controller('LaunchInstanceConfigurationController', LaunchInstanceConfigurationController);
|
||||||
|
|
||||||
LaunchInstanceConfigurationController.$inject = [
|
|
||||||
'$scope'
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc controller
|
* @ngdoc controller
|
||||||
* @name LaunchInstanceConfigurationController
|
* @name LaunchInstanceConfigurationController
|
||||||
@ -39,15 +32,9 @@
|
|||||||
* @property {string} disk_config, default to `AUTO`.
|
* @property {string} disk_config, default to `AUTO`.
|
||||||
* @property {boolean} config_drive, default to false.
|
* @property {boolean} config_drive, default to false.
|
||||||
*/
|
*/
|
||||||
function LaunchInstanceConfigurationController($scope) {
|
function LaunchInstanceConfigurationController() {
|
||||||
var ctrl = this;
|
var ctrl = this;
|
||||||
|
|
||||||
var newInstanceSpec = $scope.model.newInstanceSpec;
|
|
||||||
|
|
||||||
newInstanceSpec.user_data = DEFAULT_USER_DATA;
|
|
||||||
newInstanceSpec.disk_config = DEFAULT_DISK_CONFIG;
|
|
||||||
newInstanceSpec.config_drive = DEFAULT_CONFIG_DRIVE;
|
|
||||||
|
|
||||||
ctrl.MAX_SCRIPT_SIZE = MAX_SCRIPT_SIZE;
|
ctrl.MAX_SCRIPT_SIZE = MAX_SCRIPT_SIZE;
|
||||||
|
|
||||||
ctrl.diskConfigOptions = [
|
ctrl.diskConfigOptions = [
|
||||||
|
@ -19,20 +19,12 @@
|
|||||||
describe('Launch Instance Configuration Step', function() {
|
describe('Launch Instance Configuration Step', function() {
|
||||||
|
|
||||||
describe('LaunchInstanceConfigurationController', function() {
|
describe('LaunchInstanceConfigurationController', function() {
|
||||||
var scope, ctrl;
|
var ctrl;
|
||||||
|
|
||||||
beforeEach(module('horizon.dashboard.project.workflow.launch-instance'));
|
beforeEach(module('horizon.dashboard.project.workflow.launch-instance'));
|
||||||
|
|
||||||
beforeEach(inject(function($controller) {
|
beforeEach(inject(function($controller) {
|
||||||
scope = {
|
ctrl = $controller('LaunchInstanceConfigurationController');
|
||||||
model: {
|
|
||||||
newInstanceSpec: {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ctrl = $controller('LaunchInstanceConfigurationController', {
|
|
||||||
$scope: scope
|
|
||||||
});
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('has correct disk configuration options', function() {
|
it('has correct disk configuration options', function() {
|
||||||
@ -43,16 +35,8 @@
|
|||||||
expect(vals).toContain('MANUAL');
|
expect(vals).toContain('MANUAL');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('defaults the disk configuration to "AUTO"', function() {
|
it('sets the user data max script size', function() {
|
||||||
expect(scope.model.newInstanceSpec.disk_config).toBe('AUTO');
|
expect(ctrl.MAX_SCRIPT_SIZE).toBe(16 * 1024);
|
||||||
});
|
|
||||||
|
|
||||||
it('defaults the config_drive configuration to false', function() {
|
|
||||||
expect(scope.model.newInstanceSpec.config_drive).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('defaults the user_data configuration to ""', function() {
|
|
||||||
expect(scope.model.newInstanceSpec.user_data).toBe('');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
'horizon.app.core.openstack-service-api.novaExtensions',
|
'horizon.app.core.openstack-service-api.novaExtensions',
|
||||||
'horizon.app.core.openstack-service-api.security-group',
|
'horizon.app.core.openstack-service-api.security-group',
|
||||||
'horizon.app.core.openstack-service-api.serviceCatalog',
|
'horizon.app.core.openstack-service-api.serviceCatalog',
|
||||||
|
'horizon.app.core.openstack-service-api.settings',
|
||||||
'horizon.framework.widgets.toast.service'
|
'horizon.framework.widgets.toast.service'
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -51,6 +52,7 @@
|
|||||||
novaExtensions,
|
novaExtensions,
|
||||||
securityGroup,
|
securityGroup,
|
||||||
serviceCatalog,
|
serviceCatalog,
|
||||||
|
settings,
|
||||||
toast
|
toast
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -204,7 +206,8 @@
|
|||||||
novaAPI.getLimits().then(onGetNovaLimits, noop),
|
novaAPI.getLimits().then(onGetNovaLimits, noop),
|
||||||
securityGroup.query().then(onGetSecurityGroups, noop),
|
securityGroup.query().then(onGetSecurityGroups, noop),
|
||||||
serviceCatalog.ifTypeEnabled('network').then(getNetworks, noop),
|
serviceCatalog.ifTypeEnabled('network').then(getNetworks, noop),
|
||||||
serviceCatalog.ifTypeEnabled('volume').then(getVolumes, noop)
|
serviceCatalog.ifTypeEnabled('volume').then(getVolumes, noop),
|
||||||
|
settings.getSetting('LAUNCH_INSTANCE_DEFAULTS').then(setDefaultValues, noop)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
promise.then(onInitSuccess, onInitFail);
|
promise.then(onInitSuccess, onInitFail);
|
||||||
@ -227,6 +230,13 @@
|
|||||||
model.initialized = false;
|
model.initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setDefaultValues(defaults) {
|
||||||
|
if (!defaults) { return; }
|
||||||
|
if ('config_drive' in defaults) {
|
||||||
|
model.newInstanceSpec.config_drive = defaults.config_drive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
* @name launchInstanceModel.createInstance
|
* @name launchInstanceModel.createInstance
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
describe('Launch Instance Model', function() {
|
describe('Launch Instance Model', function() {
|
||||||
|
|
||||||
describe('launchInstanceModel Factory', function() {
|
describe('launchInstanceModel Factory', function() {
|
||||||
var model, scope, $q;
|
var model, scope, settings, $q;
|
||||||
var cinderEnabled = false;
|
var cinderEnabled = false;
|
||||||
var neutronEnabled = false;
|
var neutronEnabled = false;
|
||||||
var novaExtensionsEnabled = false;
|
var novaExtensionsEnabled = false;
|
||||||
@ -53,6 +53,14 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
settings = {
|
||||||
|
LAUNCH_INSTANCE_DEFAULTS: {
|
||||||
|
config_drive: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
$provide.value('horizon.app.core.openstack-service-api.nova', {
|
$provide.value('horizon.app.core.openstack-service-api.nova', {
|
||||||
createServer: function(finalSpec) {
|
createServer: function(finalSpec) {
|
||||||
return {
|
return {
|
||||||
@ -175,6 +183,16 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$provide.value('horizon.app.core.openstack-service-api.settings', {
|
||||||
|
getSetting: function(setting) {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
|
||||||
|
deferred.resolve(settings[setting]);
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$provide.value('horizon.framework.widgets.toast.service', {
|
$provide.value('horizon.framework.widgets.toast.service', {
|
||||||
add: function() {}
|
add: function() {}
|
||||||
});
|
});
|
||||||
@ -310,6 +328,29 @@
|
|||||||
|
|
||||||
expect(model.allowCreateVolumeFromImage).toBe(false);
|
expect(model.allowCreateVolumeFromImage).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should default config_drive to false', function() {
|
||||||
|
model.initialize(true);
|
||||||
|
scope.$apply();
|
||||||
|
|
||||||
|
expect(model.newInstanceSpec.config_drive).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should default config_drive to false if setting not provided', function() {
|
||||||
|
delete settings.LAUNCH_INSTANCE_DEFAULTS;
|
||||||
|
model.initialize(true);
|
||||||
|
scope.$apply();
|
||||||
|
|
||||||
|
expect(model.newInstanceSpec.config_drive).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should default config_drive to true based on setting', function() {
|
||||||
|
settings.LAUNCH_INSTANCE_DEFAULTS.config_drive = true;
|
||||||
|
model.initialize(true);
|
||||||
|
scope.$apply();
|
||||||
|
|
||||||
|
expect(model.newInstanceSpec.config_drive).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Post Initialization Model - Initializing', function() {
|
describe('Post Initialization Model - Initializing', function() {
|
||||||
|
@ -228,6 +228,12 @@ OPENSTACK_KEYSTONE_BACKEND = {
|
|||||||
#LAUNCH_INSTANCE_LEGACY_ENABLED = True
|
#LAUNCH_INSTANCE_LEGACY_ENABLED = True
|
||||||
#LAUNCH_INSTANCE_NG_ENABLED = False
|
#LAUNCH_INSTANCE_NG_ENABLED = False
|
||||||
|
|
||||||
|
# A dictionary of settings which can be used to provide the default values for
|
||||||
|
# properties found in the Launch Instance modal.
|
||||||
|
#LAUNCH_INSTANCE_DEFAULTS = {
|
||||||
|
# 'config_drive': False
|
||||||
|
#}
|
||||||
|
|
||||||
# The Xen Hypervisor has the ability to set the mount point for volumes
|
# The Xen Hypervisor has the ability to set the mount point for volumes
|
||||||
# attached to instances (other Hypervisors currently do not). Setting
|
# attached to instances (other Hypervisors currently do not). Setting
|
||||||
# can_set_mount_point to True will add the option to set the mount point
|
# can_set_mount_point to True will add the option to set the mount point
|
||||||
@ -687,7 +693,8 @@ SECURITY_GROUP_RULES = {
|
|||||||
# the enabled panel configuration.
|
# the enabled panel configuration.
|
||||||
# You should not add settings to this list for out of tree extensions.
|
# You should not add settings to this list for out of tree extensions.
|
||||||
# See: https://wiki.openstack.org/wiki/Horizon/RESTAPI
|
# See: https://wiki.openstack.org/wiki/Horizon/RESTAPI
|
||||||
REST_API_REQUIRED_SETTINGS = ['OPENSTACK_HYPERVISOR_FEATURES']
|
REST_API_REQUIRED_SETTINGS = ['OPENSTACK_HYPERVISOR_FEATURES',
|
||||||
|
'LAUNCH_INSTANCE_DEFAULTS']
|
||||||
|
|
||||||
# Additional settings can be made available to the client side for
|
# Additional settings can be made available to the client side for
|
||||||
# extensibility by specifying them in REST_API_ADDITIONAL_SETTINGS
|
# extensibility by specifying them in REST_API_ADDITIONAL_SETTINGS
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Added the LAUNCH_INSTANCE_DEFAULTS setting which allows specifying default
|
||||||
|
values for the Launch Instance workflow. Initially only the Configuration
|
||||||
|
Drive property is supported.
|
Loading…
Reference in New Issue
Block a user