|
@@ -30,26 +30,6 @@ App.ServerValidatorMixin = Em.Mixin.create({
|
|
|
return this.get('wizardController') && ['addServiceController', 'installerController'].contains(this.get('wizardController.name'));
|
|
|
}.property('wizardController.name'),
|
|
|
|
|
|
- /**
|
|
|
- * @type {boolean} set true if at least one config has error
|
|
|
- */
|
|
|
- configValidationError: false,
|
|
|
-
|
|
|
- /**
|
|
|
- * @type {boolean} set true if at least one config has warning
|
|
|
- */
|
|
|
- configValidationWarning: false,
|
|
|
-
|
|
|
- /**
|
|
|
- * @type {boolean} set true if at least one config has warning
|
|
|
- */
|
|
|
- configValidationFailed: false,
|
|
|
-
|
|
|
- /**
|
|
|
- * @type {object[]} contains additional message about validation errors
|
|
|
- */
|
|
|
- configValidationGlobalMessage: [],
|
|
|
-
|
|
|
/**
|
|
|
* recommendation configs loaded from server
|
|
|
* (used only during install)
|
|
@@ -64,6 +44,24 @@ App.ServerValidatorMixin = Em.Mixin.create({
|
|
|
*/
|
|
|
clusterEnvConfigs: [],
|
|
|
|
|
|
+ /**
|
|
|
+ * Collection of all config validation errors
|
|
|
+ *
|
|
|
+ * @type {Object[]}
|
|
|
+ */
|
|
|
+ configErrorList: [],
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Map with allowed error types
|
|
|
+ *
|
|
|
+ * @type {Object}
|
|
|
+ */
|
|
|
+ errorTypes: {
|
|
|
+ ERROR: 'ERROR',
|
|
|
+ WARN: 'WARN',
|
|
|
+ GENERAL: 'GENERAL'
|
|
|
+ },
|
|
|
+
|
|
|
/**
|
|
|
* by default loads data from model otherwise must be overridden as computed property
|
|
|
* refer to \assets\data\stacks\HDP-2.1\recommendations_configs.json to learn structure
|
|
@@ -103,25 +101,23 @@ App.ServerValidatorMixin = Em.Mixin.create({
|
|
|
stepConfigs: null,
|
|
|
|
|
|
serverSideValidation: function () {
|
|
|
- var deferred = $.Deferred();
|
|
|
- this.set('configValidationFailed', false);
|
|
|
- this.set('configValidationGlobalMessage', []);
|
|
|
- if (this.get('configValidationFailed')) {
|
|
|
- this.warnUser(deferred);
|
|
|
- } else {
|
|
|
- this.runServerSideValidation(deferred);
|
|
|
- }
|
|
|
- return deferred;
|
|
|
- },
|
|
|
+ var deferred = $.Deferred(),
|
|
|
+ self = this,
|
|
|
+ primary = function() { deferred.resolve(); },
|
|
|
+ secondary = function() { deferred.reject('invalid_configs'); };
|
|
|
+ this.set('configErrorList', []);
|
|
|
|
|
|
- getAllHostsWithComponents: function() {
|
|
|
- return App.ajax.send({
|
|
|
- sender: this,
|
|
|
- name: 'common.hosts.all',
|
|
|
- data: {
|
|
|
- urlParams: 'fields=HostRoles/component_name,HostRoles/host_name'
|
|
|
+ this.runServerSideValidation().done(function() {
|
|
|
+ if (self.get('configErrorList.length')) {
|
|
|
+ App.showConfigValidationPopup(self.get('configErrorList'), primary, secondary);
|
|
|
+ } else {
|
|
|
+ deferred.resolve();
|
|
|
}
|
|
|
+ }).fail(function() {
|
|
|
+ App.showConfigValidationFailedPopup(primary, secondary);
|
|
|
});
|
|
|
+
|
|
|
+ return deferred.promise();
|
|
|
},
|
|
|
|
|
|
/**
|
|
@@ -129,14 +125,15 @@ App.ServerValidatorMixin = Em.Mixin.create({
|
|
|
* send request to validate configs
|
|
|
* @returns {*}
|
|
|
*/
|
|
|
- runServerSideValidation: function (deferred) {
|
|
|
+ runServerSideValidation: function () {
|
|
|
var self = this;
|
|
|
var recommendations = this.get('hostGroups');
|
|
|
var stepConfigs = this.get('stepConfigs');
|
|
|
+ var dfd = $.Deferred();
|
|
|
|
|
|
- return this.getBlueprintConfigurations().done(function(blueprintConfigurations){
|
|
|
+ this.getBlueprintConfigurations().done(function(blueprintConfigurations) {
|
|
|
recommendations.blueprint.configurations = blueprintConfigurations;
|
|
|
- return App.ajax.send({
|
|
|
+ App.ajax.send({
|
|
|
name: 'config.validations',
|
|
|
sender: self,
|
|
|
data: {
|
|
@@ -148,10 +145,11 @@ App.ServerValidatorMixin = Em.Mixin.create({
|
|
|
},
|
|
|
success: 'validationSuccess',
|
|
|
error: 'validationError'
|
|
|
- }).complete(function () {
|
|
|
- self.warnUser(deferred);
|
|
|
+ }).complete(function() {
|
|
|
+ dfd.resolve();
|
|
|
});
|
|
|
});
|
|
|
+ return dfd.promise();
|
|
|
},
|
|
|
|
|
|
/**
|
|
@@ -206,144 +204,116 @@ App.ServerValidatorMixin = Em.Mixin.create({
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
- * @method validationSuccess
|
|
|
- * success callback after getting response from server
|
|
|
- * go through the step configs and set warn and error messages
|
|
|
+ * Creates config validation error object
|
|
|
+ *
|
|
|
+ * @param type - error type, see <code>errorTypes<code>
|
|
|
+ * @param property - config property object
|
|
|
+ * @param messages - array of messages
|
|
|
+ * @returns {{type: String, isError: boolean, isWarn: boolean, isGeneral: boolean, messages: Array}}
|
|
|
+ */
|
|
|
+ createErrorMessage: function (type, property, messages) {
|
|
|
+ var errorTypes = this.get('errorTypes');
|
|
|
+ var error = {
|
|
|
+ type: type,
|
|
|
+ isError: type === errorTypes.ERROR,
|
|
|
+ isWarn: type === errorTypes.WARN,
|
|
|
+ isGeneral: type === errorTypes.GENERAL,
|
|
|
+ messages: Em.makeArray(messages)
|
|
|
+ };
|
|
|
+
|
|
|
+ Em.assert('Unknown config error type ' + type, error.isError || error.isWarn || error.isGeneral);
|
|
|
+ if (property) {
|
|
|
+ error.serviceName = App.StackService.find(Em.get(property, 'serviceName')).get('displayName');
|
|
|
+ error.propertyName = Em.get(property, 'name');
|
|
|
+ error.filename = Em.get(property, 'filename');
|
|
|
+ error.value = Em.get(property, 'value');
|
|
|
+ error.description = Em.get(property, 'description');
|
|
|
+ }
|
|
|
+ return error;
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Parse data from server to
|
|
|
+ * <code>configErrorsMap<code> and
|
|
|
+ * <code>generalErrors<code>
|
|
|
+ *
|
|
|
* @param data
|
|
|
+ * @returns {{configErrorsMap: {}, generalErrors: Array}}
|
|
|
*/
|
|
|
- validationSuccess: function(data) {
|
|
|
- var self = this;
|
|
|
- var checkedProperties = [];
|
|
|
- var globalWarning = [];
|
|
|
- self.set('configValidationError', false);
|
|
|
- self.set('configValidationWarning', false);
|
|
|
+ parseValidation: function(data) {
|
|
|
+ var configErrorsMap = {}, generalErrors = [];
|
|
|
+
|
|
|
data.resources.forEach(function(r) {
|
|
|
r.items.forEach(function(item){
|
|
|
if (item.type == "configuration") {
|
|
|
- self.get('stepConfigs').forEach(function(service) {
|
|
|
- service.get('configs').forEach(function(property) {
|
|
|
- if ((property.get('filename') == item['config-type'] + '.xml') && (property.get('name') == item['config-name'])) {
|
|
|
- if (item.level == "ERROR") {
|
|
|
- self.set('configValidationError', true);
|
|
|
- property.set('validationErrors', property.get('validationErrors').concat(item.message).slice());
|
|
|
- } else if (item.level == "WARN") {
|
|
|
- self.set('configValidationWarning', true);
|
|
|
- property.set('validationWarnings', property.get('validationWarnings').concat(item.message).slice());
|
|
|
- }
|
|
|
- // store property data to detect WARN or ERROR messages for missed property
|
|
|
- if (["ERROR", "WARN"].contains(item.level)) checkedProperties.push(item['config-type'] + '/' + item['config-name']);
|
|
|
- }
|
|
|
- });
|
|
|
- });
|
|
|
- // check if error or warn message detected for property that absent in step configs
|
|
|
- if (["ERROR", "WARN"].contains(item.level) && !checkedProperties.contains(item['config-type'] + '/' + item['config-name'])) {
|
|
|
- var message = {
|
|
|
- propertyName: item['config-name'],
|
|
|
- filename: item['config-type'],
|
|
|
- validationWarnings: [item.message]
|
|
|
- };
|
|
|
- if (item['config-type'] === "" && item['config-name'] === "") {
|
|
|
- //service-independent validation
|
|
|
- message.isGeneral = true;
|
|
|
+ var configId = (item['config-name'] && item['config-type']) && App.config.configId(item['config-name'], item['config-type']);
|
|
|
+ if (configId) {
|
|
|
+ if (configErrorsMap[configId]) {
|
|
|
+ configErrorsMap[configId].messages.push(item.message);
|
|
|
} else {
|
|
|
- message.serviceName = App.StackService.find().filter(function(service) {
|
|
|
- return !!service.get('configTypes')[item['config-type']];
|
|
|
- })[0].get('displayName');
|
|
|
+ configErrorsMap[configId] = {
|
|
|
+ type: item.level,
|
|
|
+ messages: [item.message]
|
|
|
+ }
|
|
|
}
|
|
|
- self.set(item.level == 'WARN' ? 'configValidationWarning' : 'configValidationError', true);
|
|
|
- globalWarning.push(message);
|
|
|
+ } else {
|
|
|
+ generalErrors.push({
|
|
|
+ type: this.get('errorTypes').GENERAL,
|
|
|
+ messages: [item.message]
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
- });
|
|
|
- });
|
|
|
- self.set('configValidationGlobalMessage', globalWarning);
|
|
|
- },
|
|
|
+ }, this);
|
|
|
+ }, this);
|
|
|
|
|
|
- validationError: function (jqXHR, ajaxOptions, error, opt) {
|
|
|
- this.set('configValidationFailed', true);
|
|
|
+ return {
|
|
|
+ configErrorsMap: configErrorsMap,
|
|
|
+ generalErrors: generalErrors
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
+ /**
|
|
|
+ * Generates list of all config errors that should be displayed in popup
|
|
|
+ *
|
|
|
+ * @param configErrorsMap
|
|
|
+ * @param generalErrors
|
|
|
+ * @returns {Array}
|
|
|
+ */
|
|
|
+ collectAllIssues: function(configErrorsMap, generalErrors) {
|
|
|
+ var errorTypes = this.get('errorTypes');
|
|
|
+ var configErrorList = [];
|
|
|
+
|
|
|
+ this.get('stepConfigs').forEach(function(service) {
|
|
|
+ service.get('configs').forEach(function(property) {
|
|
|
+ if (property.get('isVisible') && !property.get('hiddenBySection')) {
|
|
|
+ var serverIssue = configErrorsMap[property.get('id')];
|
|
|
+ if (serverIssue) {
|
|
|
+ configErrorList.push(this.createErrorMessage(serverIssue.type, property, serverIssue.messages));
|
|
|
+ } else if (property.get('warnMessage')) {
|
|
|
+ configErrorList.push(this.createErrorMessage(errorTypes.WARN, property, [property.get('warnMessage')]));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, this);
|
|
|
+ }, this);
|
|
|
+
|
|
|
+ generalErrors.forEach(function(serverIssue) {
|
|
|
+ configErrorList.push(this.createErrorMessage(errorTypes.GENERAL, null, serverIssue.messages));
|
|
|
+ }, this);
|
|
|
+
|
|
|
+ return configErrorList;
|
|
|
+ },
|
|
|
|
|
|
/**
|
|
|
- * warn user if some errors or warning were
|
|
|
- * in setting up configs otherwise go to the nex operation
|
|
|
- * @param deferred
|
|
|
- * @returns {*}
|
|
|
+ * @method validationSuccess
|
|
|
+ * success callback after getting response from server
|
|
|
+ * go through the step configs and set warn and error messages
|
|
|
+ * @param data
|
|
|
*/
|
|
|
- warnUser: function(deferred) {
|
|
|
- var self = this;
|
|
|
- if (this.get('configValidationFailed')) {
|
|
|
- return App.ModalPopup.show({
|
|
|
- header: Em.I18n.t('installer.step7.popup.validation.failed.header'),
|
|
|
- primary: Em.I18n.t('common.proceedAnyway'),
|
|
|
- primaryClass: 'btn-danger',
|
|
|
- marginBottom: 200,
|
|
|
- onPrimary: function () {
|
|
|
- this.hide();
|
|
|
- deferred.resolve();
|
|
|
- },
|
|
|
- onSecondary: function () {
|
|
|
- this.hide();
|
|
|
- deferred.reject("invalid_configs"); // message used to differentiate types of rejections.
|
|
|
- },
|
|
|
- onClose: function () {
|
|
|
- this.hide();
|
|
|
- deferred.reject("invalid_configs"); // message used to differentiate types of rejections.
|
|
|
- },
|
|
|
- body: Em.I18n.t('installer.step7.popup.validation.request.failed.body')
|
|
|
- });
|
|
|
- } else if (this.get('configValidationWarning') || this.get('configValidationError')) {
|
|
|
- // Motivation: for server-side validation warnings allow user to continue wizard
|
|
|
- var stepConfigs = self.get('name') === 'mainServiceInfoConfigsController'
|
|
|
- ? [self.get('selectedService')]
|
|
|
- : self.get('stepConfigs');
|
|
|
- var configsWithErrors = stepConfigs.some(function (step) {
|
|
|
- return step.get('configs').some(function(c) {
|
|
|
- return c.get('isVisible') && !c.get('hiddenBySection') && (c.get('hasValidationWarnings') || c.get('hasValidationErrors'));
|
|
|
- });
|
|
|
- });
|
|
|
- if (configsWithErrors) {
|
|
|
- return App.ModalPopup.show({
|
|
|
- header: Em.I18n.t('installer.step7.popup.validation.warning.header'),
|
|
|
- classNames: ['sixty-percent-width-modal','modal-full-width'],
|
|
|
- primary: Em.I18n.t('common.proceedAnyway'),
|
|
|
- primaryClass: 'btn-danger',
|
|
|
- marginBottom: 200,
|
|
|
- onPrimary: function () {
|
|
|
- this.hide();
|
|
|
- deferred.resolve();
|
|
|
- },
|
|
|
- onSecondary: function () {
|
|
|
- this.hide();
|
|
|
- deferred.reject("invalid_configs"); // message used to differentiate types of rejections.
|
|
|
- },
|
|
|
- onClose: function () {
|
|
|
- this.hide();
|
|
|
- deferred.reject("invalid_configs"); // message used to differentiate types of rejections.
|
|
|
- },
|
|
|
- hide: function() {
|
|
|
- stepConfigs.forEach(function(serviceConfig) {
|
|
|
- serviceConfig.get('configs').invoke('setProperties', {
|
|
|
- validationErrors: [].slice(),
|
|
|
- validationWarnings: [].slice()
|
|
|
- });
|
|
|
- });
|
|
|
- this._super();
|
|
|
- },
|
|
|
- bodyClass: Em.View.extend({
|
|
|
- controller: self,
|
|
|
- templateName: require('templates/common/modal_popups/config_recommendation_popup'),
|
|
|
- serviceConfigs: stepConfigs,
|
|
|
- messageBody: Em.I18n.t(self.get('configValidationError')
|
|
|
- ? 'installer.step7.popup.validation.error.body'
|
|
|
- : 'installer.step7.popup.validation.warning.body')
|
|
|
- })
|
|
|
- });
|
|
|
- } else {
|
|
|
- deferred.resolve();
|
|
|
- }
|
|
|
- } else {
|
|
|
- deferred.resolve();
|
|
|
- }
|
|
|
- }
|
|
|
+ validationSuccess: function(data) {
|
|
|
+ var parsed = this.parseValidation(data);
|
|
|
+ this.set('configErrorList', this.collectAllIssues(parsed.configErrorsMap, parsed.generalErrors));
|
|
|
+ },
|
|
|
+
|
|
|
+ validationError: Em.K
|
|
|
});
|