Add VMware UI settings tab to Cluster page
Add VMware related functions to FuelWeb UI by adding a separate VMware tab to Cluster page Change-Id: Idf29b85b2793cde0fd491d26e3cb5ee99da06ad9 Implements: blueprint vmware-ui-settings
This commit is contained in:
parent
2df71c76d6
commit
b9f332bd4d
|
@ -60,7 +60,7 @@ define([
|
|||
}
|
||||
};
|
||||
|
||||
var restrictionMixin = {
|
||||
var restrictionMixin = models.restrictionMixin = {
|
||||
expandRestrictions: function(restrictions, path) {
|
||||
path = path || 'restrictions';
|
||||
this.expandedRestrictions = this.expandedRestrictions || {};
|
||||
|
|
|
@ -30,9 +30,10 @@ define(
|
|||
'jsx!views/cluster_page_tabs/settings_tab',
|
||||
'jsx!views/cluster_page_tabs/logs_tab',
|
||||
'jsx!views/cluster_page_tabs/actions_tab',
|
||||
'jsx!views/cluster_page_tabs/healthcheck_tab'
|
||||
'jsx!views/cluster_page_tabs/healthcheck_tab',
|
||||
'plugins/vmware/vmware'
|
||||
],
|
||||
function($, _, i18n, Backbone, React, utils, models, dispatcher, componentMixins, dialogs, NodesTab, NetworkTab, SettingsTab, LogsTab, ActionsTab, HealthCheckTab) {
|
||||
function($, _, i18n, Backbone, React, utils, models, dispatcher, componentMixins, dialogs, NodesTab, NetworkTab, SettingsTab, LogsTab, ActionsTab, HealthCheckTab, VmWareTab) {
|
||||
'use strict';
|
||||
|
||||
var ClusterPage, ClusterInfo, DeploymentResult, DeploymentControl,
|
||||
|
@ -172,7 +173,7 @@ function($, _, i18n, Backbone, React, utils, models, dispatcher, componentMixins
|
|||
if (this.hasChanges()) return i18n('dialog.dismiss_settings.default_message');
|
||||
},
|
||||
getAvailableTabs: function() {
|
||||
return [
|
||||
var tabs = [
|
||||
{url: 'nodes', tab: NodesTab},
|
||||
{url: 'network', tab: NetworkTab},
|
||||
{url: 'settings', tab: SettingsTab},
|
||||
|
@ -180,6 +181,13 @@ function($, _, i18n, Backbone, React, utils, models, dispatcher, componentMixins
|
|||
{url: 'healthcheck', tab: HealthCheckTab},
|
||||
{url: 'actions', tab: ActionsTab}
|
||||
];
|
||||
var settings = this.props.cluster.get('settings'),
|
||||
useVCenter = settings.get('common.use_vcenter').value,
|
||||
index = _.findIndex(tabs, {url: 'settings'});
|
||||
if (useVCenter) {
|
||||
tabs.splice(index + 1, 0, {url: 'vmware', tab: VmWareTab});
|
||||
}
|
||||
return tabs;
|
||||
},
|
||||
checkTab: function(props) {
|
||||
props = props || this.props;
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 829 B |
|
@ -0,0 +1,57 @@
|
|||
@legend-fg: #222222;
|
||||
@legend-bg: #ececec;
|
||||
|
||||
// box-sizing
|
||||
.box-sizing-mixin() {
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tab-vmware-normal {
|
||||
// TODO replace the icon with a real one
|
||||
background: url('/static/plugins/vmware/icons.png') no-repeat 0px 0px;
|
||||
}
|
||||
|
||||
.vmware {
|
||||
.parameter-description {
|
||||
padding: 10px 5px !important;
|
||||
}
|
||||
|
||||
.label-wrapper {
|
||||
margin-top: 8px;
|
||||
width: 185px;
|
||||
}
|
||||
|
||||
legend.vmware {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
background-color: @legend-bg;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
height: 42px;
|
||||
margin-top: 25px;
|
||||
font-size: 18px;
|
||||
color: @legend-fg;
|
||||
line-height: 18px;
|
||||
.box-sizing-mixin;
|
||||
}
|
||||
|
||||
.page-control-box {
|
||||
margin: 10px 20px;
|
||||
}
|
||||
|
||||
.idented {
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.password input {
|
||||
width: 176px;
|
||||
}
|
||||
|
||||
.nova-compute .btn {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"en-US": {
|
||||
"translation": {
|
||||
"cluster_page": {
|
||||
"tabs": {
|
||||
"vmware": "VMware"
|
||||
}
|
||||
},
|
||||
"vmware": {
|
||||
"title": "VMware vCenter Settings",
|
||||
"availability_zones": "vCenter",
|
||||
"network": "Network",
|
||||
"glance": "Glance",
|
||||
"cinder": "Cinder",
|
||||
"reset_to_defaults": "Load Defaults",
|
||||
"cancel": "Cancel Changes",
|
||||
"apply": "Save Changes",
|
||||
"nova_computes": "Nova Computes",
|
||||
"nova_compute": "Nova Compute Instance"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2015 Mirantis, Inc.
|
||||
*
|
||||
* 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.
|
||||
**/
|
||||
|
||||
define(
|
||||
[
|
||||
'jsx!./vmware_tab',
|
||||
'json!./translations.json',
|
||||
'less!./styles',
|
||||
'jquery',
|
||||
'underscore'
|
||||
],
|
||||
function(VmWareTab, translations, styles, $, _) {
|
||||
'use strict';
|
||||
|
||||
_.merge($.i18n.options.resStore, translations);
|
||||
|
||||
return VmWareTab;
|
||||
});
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* Copyright 2015 Mirantis, Inc.
|
||||
*
|
||||
* 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.
|
||||
**/
|
||||
|
||||
define(
|
||||
[
|
||||
'jquery',
|
||||
'underscore',
|
||||
'i18n',
|
||||
'backbone',
|
||||
'models'
|
||||
],
|
||||
function($, _, i18n, Backbone, models) {
|
||||
'use strict';
|
||||
|
||||
function isRegularField(field) {
|
||||
return _.contains(['text', 'password', 'checkbox'], field.type);
|
||||
}
|
||||
|
||||
// models for testing restrictions
|
||||
var restrictionModels = {};
|
||||
|
||||
// Test regex using regex cache
|
||||
var regexCache = {};
|
||||
function testRegex(regexText, value) {
|
||||
if (!regexCache[regexText]) {
|
||||
regexCache[regexText] = new RegExp(regexText);
|
||||
}
|
||||
return regexCache[regexText].test(value);
|
||||
}
|
||||
|
||||
var BaseModel = Backbone.Model.extend(models.restrictionMixin).extend({
|
||||
constructorName: 'BaseModel',
|
||||
toJSON: function() {
|
||||
return _.omit(this.attributes, 'metadata');
|
||||
},
|
||||
validate: function() {
|
||||
this.expandedRestrictions = this.expandedRestrictions || {};
|
||||
var result = {};
|
||||
_.each(this.attributes.metadata, function(field) {
|
||||
if (!isRegularField(field) || field.type == 'checkbox') {
|
||||
return;
|
||||
}
|
||||
var isDisabled = this.checkRestrictions(restrictionModels, undefined, field.name);
|
||||
if (isDisabled.result) {
|
||||
return;
|
||||
}
|
||||
var value = this.get(field.name);
|
||||
if (field.regex) {
|
||||
if (!testRegex(field.regex.source, value)) {
|
||||
result[field.name] = field.regex.error;
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
return _.isEmpty(result) ? null : result;
|
||||
},
|
||||
parseRestrictions: function() {
|
||||
var metadata = this.get('metadata');
|
||||
_.each(metadata, function(field) {
|
||||
var key = field.name,
|
||||
restrictions = field.restrictions || [],
|
||||
childModel = this.get(key);
|
||||
this.expandRestrictions(restrictions, key);
|
||||
|
||||
if (_.isFunction(childModel.parseRestrictions)) {
|
||||
childModel.parseRestrictions();
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
testRestrictions: function() {
|
||||
var results = {
|
||||
hide: {},
|
||||
disable: {}
|
||||
};
|
||||
var metadata = this.get('metadata');
|
||||
_.each(metadata, function(field) {
|
||||
var key = field.name;
|
||||
var disableResult = this.checkRestrictions(restrictionModels, undefined, key);
|
||||
results.disable[key] = disableResult.result;
|
||||
|
||||
var hideResult = this.checkRestrictions(restrictionModels, 'hide', key);
|
||||
results.hide[key] = hideResult.result;
|
||||
}, this);
|
||||
return results;
|
||||
}
|
||||
});
|
||||
|
||||
var BaseCollection = Backbone.Collection.extend({
|
||||
constructorName: 'BaseCollection',
|
||||
model: BaseModel,
|
||||
isValid: function() {
|
||||
this.validationError = this.validate();
|
||||
return this.validationError;
|
||||
},
|
||||
validate: function() {
|
||||
var errors = _.compact(this.models.map(function(model) {
|
||||
model.isValid();
|
||||
return model.validationError;
|
||||
}));
|
||||
return _.isEmpty(errors) ? null : errors;
|
||||
},
|
||||
parseRestrictions: function() {
|
||||
_.invoke(this.models, 'parseRestrictions');
|
||||
},
|
||||
testRestrictions: function() {
|
||||
_.invoke(this.models, 'testRestrictions', restrictionModels);
|
||||
}
|
||||
});
|
||||
|
||||
var Cinder = BaseModel.extend({constructorName: 'Cinder'});
|
||||
var NovaCompute = BaseModel.extend({constructorName: 'NovaCompute'});
|
||||
|
||||
var NovaComputes = BaseCollection.extend({
|
||||
constructorName: 'NovaComputes',
|
||||
model: NovaCompute
|
||||
});
|
||||
|
||||
var AvailabilityZone = BaseModel.extend({
|
||||
constructorName: 'AvailabilityZone',
|
||||
constructor: function(data) {
|
||||
Backbone.Model.apply(this, arguments);
|
||||
if (data) {
|
||||
this.set(this.parse(data));
|
||||
}
|
||||
},
|
||||
parse: function(response) {
|
||||
var result = {};
|
||||
var metadata = response.metadata;
|
||||
result.metadata = metadata;
|
||||
|
||||
// regular fields
|
||||
_.each(metadata, function(field) {
|
||||
if (isRegularField(field)) {
|
||||
result[field.name] = response[field.name];
|
||||
}
|
||||
}, this);
|
||||
|
||||
// nova_computes
|
||||
var novaMetadata = _.find(metadata, {name: 'nova_computes'});
|
||||
var novaValues = _.clone(response.nova_computes);
|
||||
novaValues = _.map(novaValues, function(value) {
|
||||
value.metadata = novaMetadata.fields;
|
||||
return new NovaCompute(value);
|
||||
});
|
||||
result.nova_computes = new NovaComputes(novaValues);
|
||||
|
||||
// cinder
|
||||
var cinderMetadata = _.find(metadata, {name: 'cinder'});
|
||||
var cinderValue = _.extend(_.clone(response.cinder), {metadata: cinderMetadata.fields});
|
||||
result.cinder = new Cinder(cinderValue);
|
||||
|
||||
return result;
|
||||
},
|
||||
toJSON: function() {
|
||||
var result = _.omit(this.attributes, 'metadata', 'nova_computes', 'cinder');
|
||||
result.nova_computes = this.get('nova_computes').toJSON();
|
||||
result.cinder = this.get('cinder').toJSON();
|
||||
return result;
|
||||
},
|
||||
validate: function() {
|
||||
var errors = _.merge({}, BaseModel.prototype.validate.call(this));
|
||||
|
||||
var novaComputes = this.get('nova_computes');
|
||||
novaComputes.isValid();
|
||||
if (novaComputes.validationError) {
|
||||
errors.nova_computes = novaComputes.validationError;
|
||||
}
|
||||
|
||||
var cinder = this.get('cinder');
|
||||
cinder.isValid();
|
||||
if (cinder.validationError) {
|
||||
errors.cinder = cinder.validationError;
|
||||
}
|
||||
|
||||
return _.isEmpty(errors) ? null : errors;
|
||||
}
|
||||
});
|
||||
|
||||
var AvailabilityZones = BaseCollection.extend({
|
||||
constructorName: 'AvailabilityZones',
|
||||
model: AvailabilityZone
|
||||
});
|
||||
|
||||
var Network = BaseModel.extend({constructorName: 'Network'});
|
||||
var Glance = BaseModel.extend({constructorName: 'Glance'});
|
||||
|
||||
var VCenter = BaseModel.extend({
|
||||
constructorName: 'VCenter',
|
||||
url: function() {
|
||||
return '/api/v1/clusters/' + this.id + '/vmware_attributes' + (this.loadDefaults ? '/defaults' : '');
|
||||
},
|
||||
parse: function(response) {
|
||||
if (!response.editable || !response.editable.metadata || !response.editable.value) {
|
||||
return;
|
||||
}
|
||||
var metadata = response.editable.metadata || [],
|
||||
value = response.editable.value || {};
|
||||
|
||||
// Availability Zone(s)
|
||||
var azMetadata = _.find(metadata, {name: 'availability_zones'});
|
||||
var azValues = _.clone(value.availability_zones);
|
||||
azValues = _.map(azValues, function(value) {
|
||||
value.metadata = azMetadata.fields;
|
||||
return value;
|
||||
});
|
||||
|
||||
// Network
|
||||
var networkMetadata = _.find(metadata, {name: 'network'});
|
||||
var networkValue = _.extend(_.clone(value.network), {metadata: networkMetadata.fields});
|
||||
|
||||
// Glance
|
||||
var glanceMetadata = _.find(metadata, {name: 'glance'});
|
||||
var glanceValue = _.extend(_.clone(value.glance), {metadata: glanceMetadata.fields});
|
||||
|
||||
return {
|
||||
metadata: metadata,
|
||||
availability_zones: new AvailabilityZones(azValues),
|
||||
network: new Network(networkValue),
|
||||
glance: new Glance(glanceValue)
|
||||
};
|
||||
},
|
||||
isFilled: function() {
|
||||
var result = this.get('availability_zones') && this.get('network') && this.get('glance');
|
||||
return !!result;
|
||||
},
|
||||
toJSON: function() {
|
||||
if (!this.isFilled()) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
editable: {
|
||||
value: {
|
||||
availability_zones: this.get('availability_zones').toJSON(),
|
||||
network: this.get('network').toJSON(),
|
||||
glance: this.get('glance').toJSON()
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
validate: function() {
|
||||
if (!this.isFilled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var errors = {};
|
||||
_.each(this.get('metadata'), function(field) {
|
||||
var key = field.name;
|
||||
var model = this.get(key);
|
||||
// do not validate disabled restrictions
|
||||
var isDisabled = this.checkRestrictions(restrictionModels, undefined, key);
|
||||
if (isDisabled.result) {
|
||||
return;
|
||||
}
|
||||
model.isValid();
|
||||
if (model.validationError) {
|
||||
errors[key] = model.validationError;
|
||||
}
|
||||
}, this);
|
||||
return _.isEmpty(errors) ? null : errors;
|
||||
},
|
||||
setModels: function(models) {
|
||||
restrictionModels = models;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
VCenter: VCenter,
|
||||
AvailabilityZone: AvailabilityZone,
|
||||
Network: Network,
|
||||
Glance: Glance,
|
||||
NovaCompute: NovaCompute,
|
||||
isRegularField: isRegularField
|
||||
};
|
||||
});
|
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* Copyright 2015 Mirantis, Inc.
|
||||
*
|
||||
* 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.
|
||||
**/
|
||||
|
||||
define(
|
||||
[
|
||||
'react',
|
||||
'jquery',
|
||||
'i18n',
|
||||
'underscore',
|
||||
'dispatcher',
|
||||
'jsx!views/controls',
|
||||
'jsx!component_mixins',
|
||||
'plugins/vmware/vmware_models'
|
||||
], function(React, $, i18n, _, dispatcher, controls, componentMixins, vmwareModels) {
|
||||
'use strict';
|
||||
|
||||
var cx = React.addons.classSet;
|
||||
|
||||
var Field = React.createClass({
|
||||
onChange: function(name, value) {
|
||||
this.props.model.set(name, value);
|
||||
this.setState({model: this.props.model});
|
||||
_.defer(function() {dispatcher.trigger('vcenter_model_update'); });
|
||||
},
|
||||
render: function() {
|
||||
var metadata = this.props.metadata,
|
||||
options = this.props.options || [],
|
||||
errors = this.props.model.validationError,
|
||||
errorText = errors ? errors[metadata.name] : null;
|
||||
var classes = cx({
|
||||
'settings-group table-wrapper parameter-box': true,
|
||||
password: metadata.type == 'password',
|
||||
'has-error': !!errorText
|
||||
});
|
||||
return (
|
||||
<div key={metadata.name} className={classes}>
|
||||
<controls.Input
|
||||
key={metadata.name}
|
||||
{... _.pick(metadata, 'name', 'type', 'label')}
|
||||
value={this.props.model.get(metadata.name)}
|
||||
checked={this.props.model.get(metadata.name)}
|
||||
description={errorText || metadata.description}
|
||||
descriptionClassName={cx({'validation-error': errorText})}
|
||||
toggleable={metadata.type == 'password'}
|
||||
wrapperClassName='tablerow-wrapper'
|
||||
onChange={this.onChange}
|
||||
disabled={this.props.disabled}
|
||||
>
|
||||
{options.map(function(index) {
|
||||
return <option key={index.label} value={index.value}>{index.label}</option>;
|
||||
}, this)}
|
||||
</controls.Input>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var FieldGroup = React.createClass({
|
||||
render: function() {
|
||||
var metadata = _.filter(this.props.model.get('metadata'), vmwareModels.isRegularField);
|
||||
var fields = metadata.map(function(meta) {
|
||||
return (
|
||||
<Field {... _.pick(this.props, 'model', 'disabled')} key={meta.name} metadata={meta}/>
|
||||
);
|
||||
}, this);
|
||||
return (
|
||||
<div>
|
||||
{fields}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var Cinder = React.createClass({
|
||||
render: function() {
|
||||
var model = this.props.model;
|
||||
if (!model) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className='cinder'>
|
||||
<legend className='vmware'>{i18n('vmware.cinder')}</legend>
|
||||
<FieldGroup model={model} disabled={this.props.disabled}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var NovaCompute = React.createClass({
|
||||
render: function() {
|
||||
var model = this.props.model;
|
||||
if (!model) {
|
||||
return null;
|
||||
}
|
||||
var removeButtonClasses = cx({'btn btn-link': true, hide: this.props.isRemovable});
|
||||
return (
|
||||
<div className='nova-compute'>
|
||||
<h4>
|
||||
<button className='btn btn-link'
|
||||
disabled={this.props.disabled}
|
||||
onClick={_.bind(function() {this.props.onAdd(model)}, this)}>
|
||||
<i className='icon-plus-circle'></i>
|
||||
</button>
|
||||
<button className={removeButtonClasses}
|
||||
disabled={this.props.disabled}
|
||||
onClick={_.bind(function() {this.props.onRemove(model)}, this)}>
|
||||
<i className='icon-minus-circle'></i>
|
||||
</button>
|
||||
 
|
||||
{i18n('vmware.nova_compute')}
|
||||
</h4>
|
||||
<FieldGroup model={model} disabled={this.props.disabled}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var AvailabilityZone = React.createClass({
|
||||
addNovaCompute: function(current) {
|
||||
var collection = this.props.model.get('nova_computes'),
|
||||
index = collection.indexOf(current);
|
||||
collection.add(current.clone(), {at: index + 1});
|
||||
collection.parseRestrictions();
|
||||
this.setState({model: this.props.model});
|
||||
_.defer(function() {dispatcher.trigger('vcenter_model_update'); });
|
||||
},
|
||||
removeNovaCompute: function(current) {
|
||||
var collection = this.props.model.get('nova_computes');
|
||||
collection.remove(current);
|
||||
this.setState({model: this.props.model});
|
||||
_.defer(function() { dispatcher.trigger('vcenter_model_update'); });
|
||||
},
|
||||
renderFields: function() {
|
||||
var model = this.props.model,
|
||||
meta = model.get('metadata');
|
||||
meta = _.filter(meta, vmwareModels.isRegularField);
|
||||
return (
|
||||
<FieldGroup model={model} disabled={this.props.disabled}/>
|
||||
);
|
||||
},
|
||||
renderComputes: function(actions) {
|
||||
var novaComputes = this.props.model.get('nova_computes'),
|
||||
isSingleInstance = (novaComputes.length == 1),
|
||||
disabled = actions.disable.cinder;
|
||||
|
||||
return (
|
||||
<div className='idented'>
|
||||
<legend className='vmware'>{i18n('vmware.nova_computes')}</legend>
|
||||
{
|
||||
novaComputes.map(function(value) {
|
||||
return (
|
||||
<NovaCompute
|
||||
key={value.cid}
|
||||
model={value}
|
||||
onAdd={this.addNovaCompute}
|
||||
onRemove={this.removeNovaCompute}
|
||||
isRemovable={isSingleInstance}
|
||||
disabled={disabled || this.props.disabled}
|
||||
/>
|
||||
);
|
||||
}, this)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
renderCinder: function(actions) {
|
||||
var disabled = actions.disable.cinder;
|
||||
return (
|
||||
<Cinder model={this.props.model.get('cinder')} disabled={disabled || this.props.disabled}/>
|
||||
);
|
||||
},
|
||||
render: function() {
|
||||
var restrictActions = this.props.model.testRestrictions();
|
||||
|
||||
return (
|
||||
<div>
|
||||
{this.renderFields(restrictActions)}
|
||||
{this.renderComputes(restrictActions)}
|
||||
{this.renderCinder(restrictActions)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var AvailabilityZones = React.createClass({
|
||||
render: function() {
|
||||
if (!this.props.collection) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className='availability-zones'>
|
||||
<legend className='vmware'>{i18n('vmware.availability_zones')}</legend>
|
||||
{
|
||||
this.props.collection.map(function(model) {
|
||||
return <AvailabilityZone key={model.cid} model={model} disabled={this.props.disabled}/>;
|
||||
}, this)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var Network = React.createClass({
|
||||
render: function() {
|
||||
var model = this.props.model;
|
||||
if (!model) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className='network'>
|
||||
<legend className='vmware'>{i18n('vmware.network')}</legend>
|
||||
<FieldGroup model={model} disabled={this.props.disabled}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var Glance = React.createClass({
|
||||
render: function() {
|
||||
var model = this.props.model;
|
||||
if (!model) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className='glance'>
|
||||
<legend className='vmware'>{i18n('vmware.glance')}</legend>
|
||||
<FieldGroup meta={model.get('metadata')} model={model} disabled={this.props.disabled}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var VCenter = React.createClass({
|
||||
componentDidMount: function() {
|
||||
this.clusterId = this.props.cluster.id;
|
||||
this.model = new vmwareModels.VCenter({id: this.clusterId});
|
||||
this.model.on('sync', _.bind(function() {
|
||||
this.model.parseRestrictions();
|
||||
this.actions = this.model.testRestrictions();
|
||||
if (!this.model.loadDefaults) {
|
||||
this.json = JSON.stringify(this.model.toJSON());
|
||||
}
|
||||
this.model.loadDefaults = false;
|
||||
this.setState({model: this.model});
|
||||
}, this));
|
||||
this.defaultModel = new vmwareModels.VCenter({id: this.clusterId});
|
||||
this.defaultModel.on('sync', _.bind(function() {
|
||||
this.defaultModel.parseRestrictions();
|
||||
this.defaultsJson = JSON.stringify(this.defaultModel.toJSON());
|
||||
this.setState({defaultModel: this.defaultModel});
|
||||
}, this));
|
||||
this.setState({model: this.model, defaultModel: this.defaultModel});
|
||||
|
||||
this.model.setModels({
|
||||
cluster: this.props.cluster,
|
||||
settings: this.props.cluster.get('settings')
|
||||
});
|
||||
|
||||
this.readDefaultsData();
|
||||
this.readData();
|
||||
dispatcher.on('vcenter_model_update', _.bind(function() {
|
||||
this.forceUpdate();
|
||||
}, this));
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
$(document).off('vcenter_model_update');
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {model: null};
|
||||
},
|
||||
readData: function() {
|
||||
this.model.fetch();
|
||||
},
|
||||
readDefaultsData: function() {
|
||||
this.defaultModel.loadDefaults = true;
|
||||
this.defaultModel.fetch();
|
||||
},
|
||||
saveData: function() {
|
||||
this.model.save();
|
||||
},
|
||||
onLoadDefaults: function() {
|
||||
this.model.loadDefaults = true;
|
||||
this.model.fetch().done(_.bind(function() {
|
||||
this.model.loadDefaults = false;
|
||||
}, this));
|
||||
},
|
||||
onCancel: function() {
|
||||
this.readData();
|
||||
},
|
||||
onSave: function() {
|
||||
this.saveData();
|
||||
},
|
||||
revertChanges: function() {
|
||||
this.readData();
|
||||
},
|
||||
render: function() {
|
||||
if (!this.state.model || !this.actions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var model = this.state.model,
|
||||
currentJson = JSON.stringify(this.model.toJSON()),
|
||||
editable = this.props.cluster.isAvailableForSettingsChanges(),
|
||||
// TODO hide = this.actions.hide || {},
|
||||
disable = this.actions.disable || {};
|
||||
|
||||
model.isValid();
|
||||
this.hasChanges = (this.json != currentJson);
|
||||
this.hasDefaultsChanges = (this.defaultsJson != currentJson);
|
||||
var saveDisabled = !editable || !this.hasChanges || !!model.validationError,
|
||||
defaultsDisabled = !editable || !this.hasDefaultsChanges;
|
||||
|
||||
return (
|
||||
<div className='vmware'>
|
||||
<div className='wrapper'>
|
||||
<h3>{i18n('vmware.title')}</h3>
|
||||
<AvailabilityZones collection={model.get('availability_zones')} disabled={!editable || disable.availability_zones}/>
|
||||
<Network model={model.get('network')} disabled={!editable || disable.network}/>
|
||||
<Glance model={model.get('glance')} disabled={!editable || disable.glance}/>
|
||||
</div>
|
||||
<div className='page-control-box'>
|
||||
<div className='page-control-button-placeholder'>
|
||||
<button className='btn btn-load-defaults' onClick={this.onLoadDefaults} disabled={defaultsDisabled}>
|
||||
{i18n('vmware.reset_to_defaults')}
|
||||
</button>
|
||||
<button className='btn btn-revert-changes' onClick={this.onCancel} disabled={!this.hasChanges}>
|
||||
{i18n('vmware.cancel')}
|
||||
</button>
|
||||
<button className='btn btn-success btn-apply-changes' onClick={this.onSave} disabled={saveDisabled}>
|
||||
{i18n('vmware.apply')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
return VCenter;
|
||||
});
|
Loading…
Reference in New Issue