diff --git a/openstack_dashboard/enabled/_10_project.py b/openstack_dashboard/enabled/_10_project.py index fd513e24b7..3111c4b1aa 100644 --- a/openstack_dashboard/enabled/_10_project.py +++ b/openstack_dashboard/enabled/_10_project.py @@ -37,6 +37,7 @@ ADD_JS_FILES = [ LAUNCH_INST + 'security-groups/security-groups.js', LAUNCH_INST + 'keypair/keypair.js', LAUNCH_INST + 'configuration/configuration.js', + LAUNCH_INST + 'configuration/load-edit.js', ] ADD_JS_SPEC_FILES = [ diff --git a/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.html b/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.html index 61eb09e23d..0003affdb0 100644 --- a/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.html +++ b/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.html @@ -1,57 +1,32 @@ -
-

{$ ::label.title $}

+
+

{$ ::config.label.title $}

-
{$ ::label.subtitle $}
+
{$ ::config.label.subtitle $}
-
- - -
- -
- - -
- -
- - -
- -
- -
+ +
+ +
+ +
+
diff --git a/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.js b/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.js index 1cb981515c..f898cec0e4 100644 --- a/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.js +++ b/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.js @@ -1,44 +1,93 @@ (function () { 'use strict'; + var MAX_SCRIPT_SIZE = 16 * 1024, + DEFAULT_CONFIG_DRIVE = false, + DEFAULT_USER_DATA = '', + DEFAULT_DISK_CONFIG = 'AUTO'; + + /** + * @ngdoc overview + * @name hz.dashboard.launch-instance + * @description + * + * # hz.dashboard.launch-instance + * + * The `hz.dashboard.launch-instance` module allows a user + * to launch an instance via the multi-step wizard framework + * + */ var module = angular.module('hz.dashboard.launch-instance'); + /** + * @ngdoc controller + * @name LaunchInstanceConfigurationCtrl + * @description + * The `LaunchInstanceConfigurationCtrl` controller is responsible for + * setting the following instance properties: + * + * @property {string} user_data, default to empty string. + * The maximum size of user_data is 16 * 1024. + * @property {string} disk_config, default to `AUTO`. + * @property {boolean} config_drive, default to false. + */ module.controller('LaunchInstanceConfigurationCtrl', [ '$scope', + '$element', + '$timeout', + 'wizardEvents', LaunchInstanceConfigurationCtrl ]); - module.controller('LaunchInstanceConfigurationHelpCtrl', [ - LaunchInstanceConfigurationHelpCtrl - ]); + function LaunchInstanceConfigurationCtrl( + $scope, + $element, + $timeout, + wizardEvents) { - function LaunchInstanceConfigurationCtrl($scope) { - $scope.label = { + var config = this, + newInstanceSpec = $scope.model.newInstanceSpec; + + newInstanceSpec.user_data = DEFAULT_USER_DATA; + newInstanceSpec.disk_config = DEFAULT_DISK_CONFIG; + newInstanceSpec.config_drive = DEFAULT_CONFIG_DRIVE; + + config.MAX_SCRIPT_SIZE = MAX_SCRIPT_SIZE; + + config.label = { title: gettext('Configuration'), subtitle: gettext(''), - customizationScriptSource: gettext('Customization Script Source'), customizationScript: gettext('Customization Script'), + customizationScriptMax: gettext('(Max: 16Kb)'), + loadScriptFromFile: gettext('Load script from a file'), configurationDrive: gettext('Configuration Drive'), diskPartition: gettext('Disk Partition'), - scriptFile: gettext('Script File') + scriptSize: gettext('Script size'), + scriptModified: gettext('Modified'), + scriptSizeWarningMsg: gettext('Script size > 16Kb'), + bytes: gettext('bytes'), + scriptSizeHoverWarningMsg: gettext('The maximum script size is 16Kb.') }; - $scope.scriptSourceOptions = [ - { value: 'selected', text: gettext('Select Script Source') }, - { value: 'raw', text: gettext('Direct Input') }, - { value: 'file', text: gettext('File') } - ]; - - $scope.model.newInstanceSpec.script_source = $scope.scriptSourceOptions[0].value; - - $scope.diskConfigOptions = [ + config.diskConfigOptions = [ { value: 'AUTO', text: gettext('Automatic') }, { value: 'MANUAL', text: gettext('Manual') } ]; - - $scope.model.newInstanceSpec.disk_config = $scope.diskConfigOptions[0].value; } + /** + * @ngdoc controller + * @name LaunchInstanceConfigurationHelpCtrl + * @description + * The `LaunchInstanceConfigurationHelpCtrl` controller provides functions for + * configuring the help text used within the configuration step of the + * Launch Instance Wizard. + * + */ + module.controller('LaunchInstanceConfigurationHelpCtrl', [ + LaunchInstanceConfigurationHelpCtrl + ]); + function LaunchInstanceConfigurationHelpCtrl() { var ctrl = this; @@ -49,9 +98,9 @@ ctrl.paragraphs = [ interpolate(customScriptText, customScriptMap, true), - gettext('The Customization Script Source field determines how the script information is delivered. Use Direct Input if you want to type the script directly into the Customization Script field.'), - gettext('Check the Configuration Drive box if you want to write metadata to a special configuration drive. When the instance boots, it attaches to the Configuration Drive and accesses the metadata.'), - gettext('An advanced option available when launching an instance is disk partitioning. There are two disk partition options. Selecting Automatic resizes the disk and sets it to a single partition. Selecting Manual allows you to create multiple partitions on the disk.') + gettext('Type your script directly into the Customization Script field. If your browser supports the HTML5 File API, you may choose to load your script from a file. The size of your script should not exceed 16 Kb.'), + gettext('An advanced option available when launching an instance is disk partitioning. There are two disk partition options. Selecting Automatic resizes the disk and sets it to a single partition. Selecting Manual allows you to create multiple partitions on the disk.'), + gettext('Check the Configuration Drive box if you want to write metadata to a special configuration drive. When the instance boots, it attaches to the Configuration Drive and accesses the metadata.') ]; } diff --git a/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.scss b/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.scss index 29aabd1884..a15dc7c886 100644 --- a/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.scss +++ b/openstack_dashboard/static/dashboard/launch-instance/configuration/configuration.scss @@ -1,13 +1,65 @@ -[ng-controller="LaunchInstanceConfigurationCtrl"] { +[ng-controller="LaunchInstanceConfigurationCtrl as config"] { - .customization-script-source select, - .disk-partition select { + select { width: 250px; } - .customization-script textarea { - width: 480px; - height: 280px; - font-family: Menlo, Monaco, Consolas, 'Courier New'; + textarea { + width: 100%; + height: 20em; + font-family: $code-font-family; + } + + .btn-file { + position: relative; + overflow: hidden; + + input[type=file] { + position: absolute; + top: 0; + right: 0; + min-width: 100%; + min-height: 100%; + font-size: 100px; + text-align: right; + filter: alpha(opacity=0); + opacity: 0; + outline: none; + background: white; + cursor: inherit; + display: block; + } + } + + .script-modified { + font-width: normal; + font-style: italic; + color: #888; + } + + .fa.invalid { + display: none; + } + + .size-indicator.warning { + color: $WizardValidationErrorColor; + border: none; + padding: 0; + margin: 0; + border: none; + + .fa.invalid { + display: inline; + color: $invalid-color; + cursor: pointer; + } + } + + .script-file:after, + .disk-partition:after { + content: ' '; + display: block; + clear: both; + margin-bottom: 2.5em; } } diff --git a/openstack_dashboard/static/dashboard/launch-instance/configuration/load-edit.html b/openstack_dashboard/static/dashboard/launch-instance/configuration/load-edit.html new file mode 100644 index 0000000000..4c20ab3907 --- /dev/null +++ b/openstack_dashboard/static/dashboard/launch-instance/configuration/load-edit.html @@ -0,0 +1,34 @@ +
+ + + + + {$ ::config.label.scriptSize $}: + {$ (scriptLength || 0) | number $} + {$ ::config.label.bytes $} + {$ ::config.label.customizationScriptMax $} + + +
+ +
+ + + {$ ::config.label.loadScriptFromFile $} + + +
diff --git a/openstack_dashboard/static/dashboard/launch-instance/configuration/load-edit.js b/openstack_dashboard/static/dashboard/launch-instance/configuration/load-edit.js new file mode 100644 index 0000000000..e6e144646e --- /dev/null +++ b/openstack_dashboard/static/dashboard/launch-instance/configuration/load-edit.js @@ -0,0 +1,92 @@ +(function () { + 'use strict'; + + angular.module('hz.dashboard.launch-instance') + + .directive('loadEdit', ['dashboardBasePath', '$timeout', + function (path, $timeout) { + + function link($scope, $element) { + var textarea = $element.find('textarea'), + fileInput = $element.find('input[type="file"]'), + userInput = $scope.userInput; + + $scope.textContent = ''; + + // HTML5 file API is supported by IE10+, Chrome, FireFox and Safari (on Mac). + // + // If HTML5 file API is not supported by user's browser, remove the option + // to upload a script via file upload. + $scope.config.fileApiSupported = !!FileReader; + + function onTextareaChange() { + $scope.$applyAsync(function () { + // Angular model won't provide the value of the