[Launch Instance Fix] Show volume device name
When setting a volume, allow setting the volume device name if supported Note: To test, you need to add the following to local_settings.py REST_API_REQUIRED_SETTINGS = ['OPENSTACK_HYPERVISOR_FEATURES'] OPENSTACK_HYPERVISOR_FEATURES = { 'can_set_mount_point': True } Change can_set_mount_point to False to see it not show up. Must restart the server. Co-Authored-By: Brian Tully <brian.tully@hp.com> Change-Id: Ied8b1e92ea361fa5806128a71d6815e4b1494272 Closes-Bug: #1439906
This commit is contained in:
parent
3a59bec6a7
commit
93eb310037
|
@ -304,3 +304,49 @@
|
||||||
color: $placeholder-text-color;
|
color: $placeholder-text-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-group .required label:after {
|
||||||
|
content: " *";
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-toggle {
|
||||||
|
color: #333;
|
||||||
|
background-color: #fff;
|
||||||
|
border-color: #adadad;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
background-color: #ebebeb;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #0077b3;
|
||||||
|
border-color: #006699;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled.active,
|
||||||
|
&[disabled].active {
|
||||||
|
background-color: rgba(0, 119, 179, 0.65);
|
||||||
|
border-color: rgba(0, 102, 153, 0.65);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled,
|
||||||
|
&.disabled:hover,
|
||||||
|
&.disabled:focus,
|
||||||
|
&.disabled:active,
|
||||||
|
&[disabled]:hover,
|
||||||
|
&[disabled]:focus,
|
||||||
|
&[disabled]:active,
|
||||||
|
fieldset[disabled] &:hover,
|
||||||
|
fieldset[disabled] &:focus,
|
||||||
|
fieldset[disabled] &:active,
|
||||||
|
fieldset[disabled] &.active {
|
||||||
|
background-color: #fafafa;
|
||||||
|
border-color: #ccc;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
'novaExtensions',
|
'novaExtensions',
|
||||||
'securityGroup',
|
'securityGroup',
|
||||||
'serviceCatalog',
|
'serviceCatalog',
|
||||||
|
'settingsService',
|
||||||
|
|
||||||
function (cinderAPI,
|
function (cinderAPI,
|
||||||
glanceAPI,
|
glanceAPI,
|
||||||
|
@ -58,7 +59,8 @@
|
||||||
novaAPI,
|
novaAPI,
|
||||||
novaExtensions,
|
novaExtensions,
|
||||||
securityGroup,
|
securityGroup,
|
||||||
serviceCatalog) {
|
serviceCatalog,
|
||||||
|
settingsService) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cinder: cinderAPI,
|
cinder: cinderAPI,
|
||||||
|
@ -68,35 +70,39 @@
|
||||||
nova: novaAPI,
|
nova: novaAPI,
|
||||||
novaExtensions: novaExtensions,
|
novaExtensions: novaExtensions,
|
||||||
securityGroup: securityGroup,
|
securityGroup: securityGroup,
|
||||||
serviceCatalog: serviceCatalog
|
serviceCatalog: serviceCatalog,
|
||||||
|
settingsService: settingsService
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc factory
|
* @ngdoc factory
|
||||||
* @name hz.dashboard:factory:ifExtensionsEnabled
|
* @name hz.dashboard:factory:ifFeaturesEnabled
|
||||||
* @module hz.dashboard
|
* @module hz.dashboard
|
||||||
* @kind function
|
* @kind function
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Check to see if all the listed extensions are enabled on a certain service,
|
* Check to see if all the listed features are enabled on a certain service,
|
||||||
* which is described by the service name.
|
* which is described by the service name.
|
||||||
*
|
*
|
||||||
* This is an asynchronous operation.
|
* This is an asynchronous operation.
|
||||||
*
|
*
|
||||||
* @param String serviceName The name of the service, e.g. `novaExtensions`.
|
* @param String serviceName The name of the service, e.g. `novaExtensions`.
|
||||||
* @param Array<String> extensions A list of extension's names.
|
* @param Array<String> features A list of feature's names.
|
||||||
* @return Promise the promise of the deferred task that gets resolved
|
* @return Promise the promise of the deferred task that gets resolved
|
||||||
* when all the sub-tasks are resolved.
|
* when all the sub-tasks are resolved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.factory('ifExtensionsEnabled', ['$q', 'cloudServices',
|
.factory('ifFeaturesEnabled', ['$q', 'cloudServices',
|
||||||
function ($q, cloudServices) {
|
function ($q, cloudServices) {
|
||||||
return function ifExtensionsEnabled(serviceName, extensions) {
|
return function ifFeaturesEnabled(serviceName, features) {
|
||||||
|
// each cloudServices[serviceName].ifEnabled(feature) is an asynchronous
|
||||||
|
// operation which returns a promise, thus requiring the use of $q.all
|
||||||
|
// to defer.
|
||||||
return $q.all(
|
return $q.all(
|
||||||
extensions.map(function (extension) {
|
features.map(function (feature) {
|
||||||
return cloudServices[serviceName].ifEnabled(extension);
|
return cloudServices[serviceName].ifEnabled(feature);
|
||||||
})
|
})
|
||||||
);//return
|
);//return
|
||||||
};//return
|
};//return
|
||||||
|
@ -114,35 +120,41 @@
|
||||||
* based on `serviceName`.
|
* based on `serviceName`.
|
||||||
*
|
*
|
||||||
* @param String serviceName The name of the service, e.g. `novaExtensions`.
|
* @param String serviceName The name of the service, e.g. `novaExtensions`.
|
||||||
|
* @param String attrName The name of the attribute in the service.
|
||||||
* @return Object a directive specification object that can be used to
|
* @return Object a directive specification object that can be used to
|
||||||
* create an angular directive.
|
* create an angular directive.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.factory('createDirectiveSpec', ['ifExtensionsEnabled',
|
.factory('createDirectiveSpec', ['ifFeaturesEnabled',
|
||||||
function (ifExtensionsEnabled) {
|
function (ifFeaturesEnabled) {
|
||||||
return function createDirectiveSpec(serviceName) {
|
return function createDirectiveSpec(serviceName, attrName) {
|
||||||
|
|
||||||
|
function link(scope, element, attrs, ctrl, transclude) {
|
||||||
|
element.addClass('ng-hide');
|
||||||
|
var features = fromJson(attrs[attrName]);
|
||||||
|
if (isArray(features)) {
|
||||||
|
ifFeaturesEnabled(serviceName, features).then(
|
||||||
|
// if the feature is enabled:
|
||||||
|
function () {
|
||||||
|
element.removeClass('ng-hide');
|
||||||
|
},
|
||||||
|
// if the feature is not enabled:
|
||||||
|
function () {
|
||||||
|
element.remove();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
transclude(scope, function (clone) {
|
||||||
|
element.append(clone);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
link: link,
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
transclude: true,
|
transclude: true
|
||||||
link: function link(scope, element, attrs, ctrl, transclude) {
|
};
|
||||||
element.addClass('ng-hide');
|
};
|
||||||
var extensions = fromJson(attrs.requiredExtensions);
|
|
||||||
if (isArray(extensions)) {
|
|
||||||
ifExtensionsEnabled(serviceName, extensions).then(
|
|
||||||
function () {
|
|
||||||
element.removeClass('ng-hide');
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
element.remove();
|
|
||||||
}
|
|
||||||
);//if-then
|
|
||||||
}
|
|
||||||
transclude(scope, function (clone) {
|
|
||||||
element.append(clone);
|
|
||||||
});
|
|
||||||
}//link
|
|
||||||
};//return
|
|
||||||
};//return
|
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -158,7 +170,7 @@
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
```html
|
```html
|
||||||
<nova-extension required-extensions='["config_drive"]'>
|
<nova-extension required-extensions='["config_drive"]'>
|
||||||
<div class="checkbox customization-script-source">
|
<div class="checkbox customization-script-source">
|
||||||
<label>
|
<label>
|
||||||
|
@ -181,12 +193,37 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</nova-extension>
|
</nova-extension>
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.directive('novaExtension', ['createDirectiveSpec',
|
.directive('novaExtension', ['createDirectiveSpec',
|
||||||
function (createDirectiveSpec) {
|
function (createDirectiveSpec) {
|
||||||
return createDirectiveSpec('novaExtensions');
|
return createDirectiveSpec('novaExtensions', 'requiredExtensions');
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc directive
|
||||||
|
* @name hz.dashboard:directive:settingsService
|
||||||
|
* @module hz.dashboard
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* This is to enable specifying conditional UI in a declarative way.
|
||||||
|
* Some UI components should be showing only when some certain settings
|
||||||
|
* are enabled on `settingsService` service.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
```html
|
||||||
|
<settings-service required-settings='["something"]'>
|
||||||
|
<!-- ui code here -->
|
||||||
|
</settings-service>
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
|
||||||
|
.directive('settingsService', ['createDirectiveSpec',
|
||||||
|
function (createDirectiveSpec) {
|
||||||
|
return createDirectiveSpec('settingsService', 'requiredSettings');
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
$provide.value('novaExtensions', {});
|
$provide.value('novaExtensions', {});
|
||||||
$provide.value('securityGroup', {});
|
$provide.value('securityGroup', {});
|
||||||
$provide.value('serviceCatalog', {});
|
$provide.value('serviceCatalog', {});
|
||||||
|
$provide.value('settingsService', {});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(inject(function ($injector) {
|
beforeEach(inject(function ($injector) {
|
||||||
|
@ -69,14 +70,18 @@
|
||||||
expect(cloudServices.novaExtensions).toBeDefined();
|
expect(cloudServices.novaExtensions).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should have `cloudServices.settingsService` defined.', function () {
|
||||||
|
expect(cloudServices.settingsService).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// factory:ifExtensionsEnabled
|
// factory:ifFeaturesEnabled
|
||||||
//
|
//
|
||||||
|
|
||||||
describe('factory:ifExtensionsEnabled', function () {
|
describe('factory:ifFeaturesEnabled', function () {
|
||||||
var ifExtensionsEnabled,
|
var ifFeaturesEnabled,
|
||||||
$q,
|
$q,
|
||||||
cloudServices;
|
cloudServices;
|
||||||
|
|
||||||
|
@ -103,44 +108,44 @@
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(inject(function ($injector) {
|
beforeEach(inject(function ($injector) {
|
||||||
ifExtensionsEnabled = $injector.get('ifExtensionsEnabled');
|
ifFeaturesEnabled = $injector.get('ifFeaturesEnabled');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should have `ifExtensionsEnabled` defined as a function.', function () {
|
it('should have `ifFeaturesEnabled` defined as a function.', function () {
|
||||||
expect(ifExtensionsEnabled).toBeDefined();
|
expect(ifFeaturesEnabled).toBeDefined();
|
||||||
expect(angular.isFunction(ifExtensionsEnabled)).toBe(true);
|
expect(angular.isFunction(ifFeaturesEnabled)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call $q.all() and someService.ifEnabled() when invoking ifExtensionsEnabled().', function () {
|
it('should call $q.all() and someService.ifEnabled() when invoking ifFeaturesEnabled().', function () {
|
||||||
var extensions = ['ext1', 'ext2'];
|
var extensions = ['ext1', 'ext2'];
|
||||||
ifExtensionsEnabled('someService', extensions);
|
ifFeaturesEnabled('someService', extensions);
|
||||||
expect($q.all).toHaveBeenCalled();
|
expect($q.all).toHaveBeenCalled();
|
||||||
expect(cloudServices.someService.ifEnabled).toHaveBeenCalled();
|
expect(cloudServices.someService.ifEnabled).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw when passing in an empty extensions list.', function () {
|
it('should not throw when passing in an empty extensions list.', function () {
|
||||||
expect(function () {
|
expect(function () {
|
||||||
ifExtensionsEnabled('someService', []);
|
ifFeaturesEnabled('someService', []);
|
||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw when extensions is null or undefined or not an array', function () {
|
it('should throw when extensions is null or undefined or not an array', function () {
|
||||||
expect(function () {
|
expect(function () {
|
||||||
ifExtensionsEnabled('someService', null);
|
ifFeaturesEnabled('someService', null);
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
|
|
||||||
expect(function () {
|
expect(function () {
|
||||||
ifExtensionsEnabled('someService');
|
ifFeaturesEnabled('someService');
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
|
|
||||||
expect(function () {
|
expect(function () {
|
||||||
ifExtensionsEnabled('123');
|
ifFeaturesEnabled('123');
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not throw when the provided serviceName is not a key in the services hash table', function () {
|
it('should not throw when the provided serviceName is not a key in the services hash table', function () {
|
||||||
expect(function () {
|
expect(function () {
|
||||||
ifExtensionsEnabled('invlidServiceName', []);
|
ifFeaturesEnabled('invlidServiceName', []);
|
||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -151,16 +156,16 @@
|
||||||
|
|
||||||
describe('factory:createDirectiveSpec', function () {
|
describe('factory:createDirectiveSpec', function () {
|
||||||
var createDirectiveSpec,
|
var createDirectiveSpec,
|
||||||
ifExtensionsEnabled;
|
ifFeaturesEnabled;
|
||||||
|
|
||||||
beforeEach(module('hz.dashboard', function ($provide) {
|
beforeEach(module('hz.dashboard', function ($provide) {
|
||||||
ifExtensionsEnabled = function () {
|
ifFeaturesEnabled = function () {
|
||||||
return {
|
return {
|
||||||
then: function (successCallback, errorCallback) {
|
then: function (successCallback, errorCallback) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
$provide.value('ifExtensionsEnabled', ifExtensionsEnabled);
|
$provide.value('ifFeaturesEnabled', ifFeaturesEnabled);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(inject(function ($injector) {
|
beforeEach(inject(function ($injector) {
|
||||||
|
@ -176,7 +181,7 @@
|
||||||
var directiveSpec;
|
var directiveSpec;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
directiveSpec = createDirectiveSpec('someService');
|
directiveSpec = createDirectiveSpec('someService', 'someFeature');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be defined.', function () {
|
it('should be defined.', function () {
|
||||||
|
@ -215,7 +220,7 @@
|
||||||
element;
|
element;
|
||||||
|
|
||||||
beforeEach(module('hz.dashboard', function ($provide) {
|
beforeEach(module('hz.dashboard', function ($provide) {
|
||||||
$provide.value('ifExtensionsEnabled', function () {
|
$provide.value('ifFeaturesEnabled', function () {
|
||||||
return {
|
return {
|
||||||
then: function (successCallback, errorCallback) {
|
then: function (successCallback, errorCallback) {
|
||||||
$timeout(successCallback);
|
$timeout(successCallback);
|
||||||
|
@ -252,6 +257,58 @@
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// directive:settingsService
|
||||||
|
//
|
||||||
|
|
||||||
|
describe('directive:settingsService', function () {
|
||||||
|
var $timeout,
|
||||||
|
$scope,
|
||||||
|
html = [
|
||||||
|
'<settings-service required-settings=\'["something"]\'>',
|
||||||
|
'<div class="child-element">',
|
||||||
|
'</div>',
|
||||||
|
'</settings-service>'
|
||||||
|
].join(''),
|
||||||
|
element;
|
||||||
|
|
||||||
|
beforeEach(module('hz.dashboard', function ($provide) {
|
||||||
|
$provide.value('ifFeaturesEnabled', function () {
|
||||||
|
return {
|
||||||
|
then: function (successCallback, errorCallback) {
|
||||||
|
$timeout(successCallback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(inject(function ($injector) {
|
||||||
|
var $compile = $injector.get('$compile');
|
||||||
|
$scope = $injector.get('$rootScope').$new();
|
||||||
|
$timeout = $injector.get('$timeout');
|
||||||
|
element = $compile(html)($scope);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should be compiled.', function () {
|
||||||
|
expect(element.hasClass('ng-scope')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have class name `ng-hide` by default.', function () {
|
||||||
|
expect(element.hasClass('ng-hide')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have no class name `ng-hide` after an asyncs callback.', function () {
|
||||||
|
$timeout(function () {
|
||||||
|
expect(element.hasClass('ng-hide')).toBe(false);
|
||||||
|
});
|
||||||
|
$timeout.flush();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have the right child element.', function () {
|
||||||
|
expect(element.children().first().hasClass('child-element')).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
;})();
|
;})();
|
||||||
|
|
|
@ -95,15 +95,31 @@
|
||||||
|
|
||||||
<div class="col-xs-12 col-sm-3">
|
<div class="col-xs-12 col-sm-3">
|
||||||
<div class="form-group create-volume">
|
<div class="form-group create-volume">
|
||||||
<div class="checkbox">
|
<label class="on-top">{$ ::label.volumeCreate $}</label>
|
||||||
<label class="on-top">
|
<div class="form-field">
|
||||||
<input type="checkbox" ng-model="model.newInstanceSpec.vol_create">
|
<div class="btn-group">
|
||||||
{$ ::label.volumeCreate $}</label>
|
<label class="btn btn-toggle"
|
||||||
|
ng-repeat="option in toggleButtonOptions"
|
||||||
|
ng-model="model.newInstanceSpec.vol_create"
|
||||||
|
btn-radio="option.value">{$ ::option.label $}</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-xs-12 col-sm-4 volume-size-wrapper" ng-if="model.newInstanceSpec.vol_create == true">
|
<settings-service required-settings='["OPENSTACK_HYPERVISOR_FEATURES.can_set_mount_point"]'
|
||||||
|
ng-if="model.newInstanceSpec.vol_create === true">
|
||||||
|
<div class="col-xs-12 col-sm-3">
|
||||||
|
<div class="form-field">
|
||||||
|
<label>{$ ::label.volumeDeviceName $}</label>
|
||||||
|
<input class="form-control input-sm"
|
||||||
|
ng-model="model.newInstanceSpec.vol_device_name"
|
||||||
|
type="text">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</settings-service>
|
||||||
|
|
||||||
|
<div class="col-xs-12 col-sm-2 volume-size-wrapper" ng-if="model.newInstanceSpec.vol_create == true">
|
||||||
<div class="form-field volume-size"
|
<div class="form-field volume-size"
|
||||||
ng-class="{ 'has-warning': launchInstanceSourceForm['volume-size'].$invalid }">
|
ng-class="{ 'has-warning': launchInstanceSourceForm['volume-size'].$invalid }">
|
||||||
<label class="on-top">{$ ::label.volumeSize $}</label>
|
<label class="on-top">{$ ::label.volumeSize $}</label>
|
||||||
|
@ -122,11 +138,14 @@
|
||||||
|
|
||||||
<div class="col-xs-12 col-sm-4" ng-if="model.newInstanceSpec.vol_create == true">
|
<div class="col-xs-12 col-sm-4" ng-if="model.newInstanceSpec.vol_create == true">
|
||||||
<div class="form-group delete-volume">
|
<div class="form-group delete-volume">
|
||||||
<div class="checkbox">
|
<label class="on-top">{$ ::label.deleteVolumeOnTerminate $}</label>
|
||||||
<label class="on-top">
|
<div class="form-field">
|
||||||
<input type="checkbox"
|
<div class="btn-group">
|
||||||
ng-model="model.newInstanceSpec.vol_delete_on_terminate">
|
<label class="btn btn-toggle"
|
||||||
{$ ::label.deleteVolumeOnTerminate $}</label>
|
ng-repeat="option in toggleButtonOptions"
|
||||||
|
ng-model="model.newInstanceSpec.vol_delete_on_terminate"
|
||||||
|
btn-radio="option.value">{$ ::option.label $}</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -136,14 +155,19 @@
|
||||||
<div class="col-xs-12 col-sm-9"
|
<div class="col-xs-12 col-sm-9"
|
||||||
ng-if="model.newInstanceSpec.source_type.type == 'volume' || model.newInstanceSpec.source_type.type == 'volume_snapshot'">
|
ng-if="model.newInstanceSpec.source_type.type == 'volume' || model.newInstanceSpec.source_type.type == 'volume_snapshot'">
|
||||||
<div class="col-xs-12 col-sm-6">
|
<div class="col-xs-12 col-sm-6">
|
||||||
|
|
||||||
<div class="form-group delete-volume">
|
<div class="form-group delete-volume">
|
||||||
<div class="checkbox">
|
<label class="on-top">{$ ::label.deleteVolumeOnTerminate $}</label>
|
||||||
<label class="on-top">
|
<div class="form-field">
|
||||||
<input type="checkbox"
|
<div class="btn-group">
|
||||||
ng-model="model.newInstanceSpec.vol_delete_on_terminate">
|
<label class="btn btn-toggle"
|
||||||
{$ ::label.deleteVolumeOnTerminate $}</label>
|
ng-repeat="option in toggleButtonOptions"
|
||||||
|
ng-model="model.newInstanceSpec.vol_delete_on_terminate"
|
||||||
|
btn-radio="option.value">{$ ::option.label $}</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div><!-- end volume select options -->
|
</div><!-- end volume select options -->
|
||||||
|
|
||||||
|
|
|
@ -83,9 +83,9 @@
|
||||||
instanceSourceTitle: gettext('Instance Source'),
|
instanceSourceTitle: gettext('Instance Source'),
|
||||||
instanceSourceSubTitle: gettext('Instance source is the template used to create an instance. You can use a snapshot of an existing instance, an image, or a volume (if enabled). You can also choose to use persistent storage by creating a new volume.'),
|
instanceSourceSubTitle: gettext('Instance source is the template used to create an instance. You can use a snapshot of an existing instance, an image, or a volume (if enabled). You can also choose to use persistent storage by creating a new volume.'),
|
||||||
bootSource: gettext('Select Boot Source'),
|
bootSource: gettext('Select Boot Source'),
|
||||||
deviceSize: gettext('Device Size (GB)'),
|
volumeSize: gettext('Size (GB)'),
|
||||||
volumeSize: gettext('Volume Size (GB)'),
|
|
||||||
volumeCreate: gettext('Create New Volume'),
|
volumeCreate: gettext('Create New Volume'),
|
||||||
|
volumeDeviceName: gettext('Device Name'),
|
||||||
deleteVolumeOnTerminate: gettext('Delete Volume on Terminate'),
|
deleteVolumeOnTerminate: gettext('Delete Volume on Terminate'),
|
||||||
id: gettext('ID'),
|
id: gettext('ID'),
|
||||||
min_ram: gettext('Min Ram'),
|
min_ram: gettext('Min Ram'),
|
||||||
|
@ -99,6 +99,13 @@
|
||||||
$scope.instanceCountError = gettext('Instance count is required and must be an integer of at least 1');
|
$scope.instanceCountError = gettext('Instance count is required and must be an integer of at least 1');
|
||||||
$scope.volumeSizeError = gettext('Volume size is required and must be an integer');
|
$scope.volumeSizeError = gettext('Volume size is required and must be an integer');
|
||||||
|
|
||||||
|
|
||||||
|
// toggle button label/value defaults
|
||||||
|
$scope.toggleButtonOptions = [
|
||||||
|
{ label: gettext('Yes'), value: true },
|
||||||
|
{ label: gettext('No'), value: false }
|
||||||
|
];
|
||||||
|
|
||||||
//
|
//
|
||||||
// Boot Sources
|
// Boot Sources
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,26 +1,3 @@
|
||||||
.form-group .required label:after {
|
|
||||||
content: " *";
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-source {
|
|
||||||
background: #eee;
|
|
||||||
padding: 12px 18px;
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
.chart {
|
|
||||||
width: 99%;
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
border-left: 1px solid #ccc;
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ng-controller="LaunchInstanceSourceCtrl"] {
|
[ng-controller="LaunchInstanceSourceCtrl"] {
|
||||||
|
|
||||||
td.hi-light {
|
td.hi-light {
|
||||||
|
@ -32,22 +9,36 @@
|
||||||
text-align: right;
|
text-align: right;
|
||||||
padding-right: 30px;
|
padding-right: 30px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.instance-source {
|
.selected-source {
|
||||||
margin-top: 18px;
|
background: #eee;
|
||||||
margin-bottom: 40px;
|
padding: 12px 18px;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
.image select {
|
.chart {
|
||||||
width: 99%;
|
width: 99%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
border-left: 1px solid #ccc;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.volume-size input[type="number"]{
|
.instance-source {
|
||||||
width: 90%;
|
margin-top: 18px;
|
||||||
}
|
margin-bottom: 40px;
|
||||||
|
|
||||||
|
.image select {
|
||||||
|
width: 99%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.volume-size input[type="number"]{
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
.create-volume,
|
|
||||||
.delete-volume {
|
|
||||||
margin-top: 1.7em;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue