You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ab...@apache.org on 2015/01/22 15:57:39 UTC
[2/3] ambari git commit: AMBARI-9264 Stack and Versions: Integrate
view/edit repositories with API. (ababiichuk)
AMBARI-9264 Stack and Versions: Integrate view/edit repositories with API. (ababiichuk)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/11efa2f5
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/11efa2f5
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/11efa2f5
Branch: refs/heads/trunk
Commit: 11efa2f5580b28acb3484586c2f74d5b9adfc17d
Parents: 5f4adaa
Author: aBabiichuk <ab...@cybervisiontech.com>
Authored: Thu Jan 22 16:41:20 2015 +0200
Committer: aBabiichuk <ab...@cybervisiontech.com>
Committed: Thu Jan 22 16:41:20 2015 +0200
----------------------------------------------------------------------
ambari-web/app/assets/test/tests.js | 2 +-
.../main/admin/stack_and_upgrade_controller.js | 111 +-
ambari-web/app/messages.js | 1 +
.../admin/stack_upgrade/edit_repositories.hbs | 20 +-
ambari-web/app/utils/ajax/ajax.js | 26 +
ambari-web/app/views.js | 2 +-
ambari-web/app/views/common/controls_view.js | 1224 ++++++++++++++++++
.../stack_upgrade/upgrade_version_box_view.js | 40 +-
ambari-web/app/views/wizard/controls_view.js | 1208 -----------------
.../admin/stack_and_upgrade_controller_test.js | 50 +
.../test/views/common/controls_view_test.js | 606 +++++++++
.../test/views/wizard/controls_view_test.js | 606 ---------
12 files changed, 2064 insertions(+), 1832 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/11efa2f5/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js
index 33abd6e..d02a375 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -211,10 +211,10 @@ var files = ['test/init_model_test',
'test/views/common/configs/service_config_container_view_test',
'test/views/common/configs/service_configs_by_category_view_test',
'test/views/common/configs/custom_category_views/notification_configs_view_test',
+ 'test/views/common/controls_view_test',
'test/views/wizard/step3/hostLogPopupBody_view_test',
'test/views/wizard/step3/hostWarningPopupBody_view_test',
'test/views/wizard/step3/hostWarningPopupFooter_view_test',
- 'test/views/wizard/controls_view_test',
'test/views/wizard/step0_view_test',
'test/views/wizard/step1_view_test',
'test/views/wizard/step2_view_test',
http://git-wip-us.apache.org/repos/asf/ambari/blob/11efa2f5/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js b/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
index 232826d..d98a816 100644
--- a/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
+++ b/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
@@ -401,8 +401,115 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
});
},
- saveRepoOS: function () {
- //TODO integrate with API
+ /**
+ * transform repo data into json for
+ * saving changes to repository version
+ * @param {Em.Object} repo
+ * @returns {{operating_systems: Array}}
+ */
+ prepareRepoForSaving: function(repo) {
+ var repoVersion = { "operating_systems": [] };
+
+ repo.get('operatingSystems').forEach(function (os, k) {
+ repoVersion.operating_systems.push({
+ "OperatingSystems": {
+ "os_type": os.get("osType")
+ },
+ "repositories": []
+ });
+ os.get('repositories').forEach(function (repository) {
+ repoVersion.operating_systems[k].repositories.push({
+ "Repositories": {
+ "base_url": repository.get('baseUrl'),
+ "repo_id": repository.get('repoId'),
+ "repo_name": repository.get('repoName')
+ }
+ });
+ });
+ });
+ return repoVersion;
+ },
+
+ /**
+ * perform validation if <code>skip<code> is false and run save if
+ * validation successfull or run save without validation is <code>skip<code> is true
+ * @param {Em.Object} repo
+ * @param {boolean} skip
+ * @returns {$.Deferred}
+ */
+ saveRepoOS: function (repo, skip) {
+ var self = this;
+ var deferred = $.Deferred();
+ this.validateRepoVersions(repo, skip).done(function(data) {
+ if (data.length > 0) {
+ deferred.resolve(data);
+ } else {
+ var repoVersion = self.prepareRepoForSaving(repo);
+
+ App.ajax.send({
+ name: 'admin.stack_versions.edit.repo',
+ sender: this,
+ data: {
+ stackName: App.get('currentStackName'),
+ stackVersion: App.get('currentStackVersionNumber'),
+ repoVersionId: repo.get('repoVersionId'),
+ repoVersion: repoVersion
+ }
+ }).success(function() {
+ deferred.resolve([]);
+ });
+ }
+ });
+ return deferred.promise();
+ },
+
+ /**
+ * send request for validation for each repository
+ * @param {Em.Object} repo
+ * @param {boolean} skip
+ * @returns {*}
+ */
+ validateRepoVersions: function(repo, skip) {
+ var deferred = $.Deferred(),
+ totalCalls = 0,
+ invalidUrls = [];
+
+ if (skip) {
+ deferred.resolve(invalidUrls);
+ } else {
+ repo.get('operatingSystems').forEach(function (os) {
+ if (os.get('isSelected')) {
+ os.get('repositories').forEach(function (repo) {
+ totalCalls++;
+ App.ajax.send({
+ name: 'admin.stack_versions.validate.repo',
+ sender: this,
+ data: {
+ repo: repo,
+ repoId: repo.get('repoId'),
+ baseUrl: repo.get('baseUrl'),
+ osType: os.get('osType'),
+ stackName: App.get('currentStackName'),
+ stackVersion: App.get('currentStackVersionNumber')
+ }
+ })
+ .success(function () {
+ totalCalls--;
+ if (totalCalls === 0) deferred.resolve(invalidUrls);
+ })
+ .error(function () {
+ repo.set('hasError', true);
+ invalidUrls.push(repo);
+ totalCalls--;
+ if (totalCalls === 0) deferred.resolve(invalidUrls);
+ });
+ });
+ } else {
+ return deferred.resolve(invalidUrls);
+ }
+ });
+ }
+ return deferred.promise();
},
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/11efa2f5/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index d8026a6..548bb4c 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -1311,6 +1311,7 @@ Em.I18n.translations = {
'admin.stackVersions.filter.current': "Current ({0})",
'admin.stackVersions.editRepositories.info': 'Provide Base URLs for the Operating Systems you are configuring. Uncheck all other Operating Systems.',
+ 'admin.stackVersions.editRepositories.validation.warning': 'Some of the repositories failed validation. Make changes to the base url or skip validation if you are sure that urls are correct',
'admin.stackVersions.version.install.confirm': 'You are about to install version <strong>{0}</strong> on all hosts.',
'admin.stackVersions.version.linkTooltip': 'Click to Edit Repositories',
'admin.stackVersions.version.hostsTooltip': 'Click to List Hosts',
http://git-wip-us.apache.org/repos/asf/ambari/blob/11efa2f5/ambari-web/app/templates/main/admin/stack_upgrade/edit_repositories.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/admin/stack_upgrade/edit_repositories.hbs b/ambari-web/app/templates/main/admin/stack_upgrade/edit_repositories.hbs
index e3fbd9d..dc65f01 100644
--- a/ambari-web/app/templates/main/admin/stack_upgrade/edit_repositories.hbs
+++ b/ambari-web/app/templates/main/admin/stack_upgrade/edit_repositories.hbs
@@ -19,7 +19,9 @@
<div class="alert alert-info">
{{t admin.stackVersions.editRepositories.info}}
</div>
-
+<div {{bindAttr class="view.parentView.hasErrors::hidden :alert :alert-warning"}}>
+ {{t admin.stackVersions.editRepositories.validation.warning}}
+</div>
<div class="row-fluid">
<div class="span2"><strong>{{t common.os}}</strong></div>
<div class="span10 row-fluid">
@@ -30,13 +32,13 @@
{{#each os in view.content.operatingSystems}}
<div class="row-fluid os-block">
<div class="span2">
- {{view Ember.Checkbox class="align-checkbox" checkedBinding="os.isSelected"}} {{os.osType}}
+ {{view view.OSCheckBox osBinding="os"}} {{os.osType}}
</div>
- <div class="span10">
- {{#each repository in os.repositories}}
- <div class="row-fluid">
- <div class="span3">{{repository.repoName}}</div>
- <div class="span9">{{view Ember.TextField valueBinding="repository.baseUrl" disabledBinding="os.isDisabled"}}</div>
+ <div class="span10">
+ {{#each repository in os.repositories}}
+ <div class="row-fluid">
+ <div class="span3">{{repository.repoName}}</div>
+ <div {{bindAttr class="repository.hasError:error :control-group :span9"}}>{{view App.BaseUrlTextField repositoryBinding="repository" disabledBinding="os.isDisabled"}}</div>
</div>
{{/each}}
</div>
@@ -44,7 +46,7 @@
{{/each}}
<div>
- <label>{{view Ember.Checkbox checkedBinding="view.skipValidation" class="align-checkbox"}}{{t installer.step1.advancedRepo.skipValidation.message}}
+ <label>{{view view.skipCheckBox checkedBinding="view.parentView.skipValidation"}}{{t installer.step1.advancedRepo.skipValidation.message}}
<i class="icon-question-sign" rel="skip-validation-tooltip"
- data-toggle="tooltip" {{translateAttr title="installer.step1.advancedRepo.skipValidation.tooltip"}}></i></label>
+ data-toggle="tooltip" {{translateAttr title="installer.step1.advancedRepo.skipValidation.tooltip"}}></i></label>
</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/11efa2f5/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index 2e199b5..1f343dc 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -1393,6 +1393,32 @@ var urls = {
},
'mock': ''
},
+
+ 'admin.stack_versions.edit.repo': {
+ 'real': '/stacks/{stackName}/versions/{stackVersion}/repository_versions/{repoVersionId}',
+ 'mock': '',
+ 'type': 'PUT',
+ 'format': function (data) {
+ return {
+ data: JSON.stringify(data.repoVersion)
+ }
+ }
+ },
+ 'admin.stack_versions.validate.repo': {
+ 'real': '/stacks/{stackName}/versions/{stackVersion}/operating_systems/{osType}/repositories/{repoId}?validate_only=true',
+ 'mock': '',
+ 'type': 'POST',
+ 'format': function (data) {
+ return {
+ data: JSON.stringify({
+ "Repositories": {
+ "base_url": data.baseUrl
+ }
+ })
+ }
+ }
+ },
+
'admin.rolling_upgrade.pre_upgrade_check': {
'real': '/clusters/{clusterName}/rolling_upgrades_check?fields=*&UpgradeChecks/repository_version={value}',
'mock': '/data/stack_versions/pre_upgrade_check.json'
http://git-wip-us.apache.org/repos/asf/ambari/blob/11efa2f5/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index eb3a284..f981568 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -53,6 +53,7 @@ require('views/common/filter_combobox');
require('views/common/filter_combo_cleanable');
require('views/common/table_view');
require('views/common/progress_bar_view');
+require('views/common/controls_view');
require('views/login');
require('views/main');
require('views/main/menu');
@@ -278,7 +279,6 @@ require('views/main/charts/heatmap/heatmap_host_detail');
require('views/main/views_view');
require('views/installer');
-require('views/wizard/controls_view');
require('views/wizard/step0_view');
require('views/wizard/step1_view');
require('views/wizard/step2_view');
http://git-wip-us.apache.org/repos/asf/ambari/blob/11efa2f5/ambari-web/app/views/common/controls_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/controls_view.js b/ambari-web/app/views/common/controls_view.js
new file mode 100644
index 0000000..7a85ad4
--- /dev/null
+++ b/ambari-web/app/views/common/controls_view.js
@@ -0,0 +1,1224 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+var App = require('app');
+
+/**
+ * Abstract view for config fields.
+ * Add popover support to control
+ */
+App.ServiceConfigPopoverSupport = Ember.Mixin.create({
+
+ /**
+ * Config object. It will instance of App.ServiceConfigProperty
+ */
+ serviceConfig: null,
+ attributeBindings:['readOnly'],
+ isPopoverEnabled: true,
+
+ didInsertElement: function () {
+ $('body').tooltip({
+ selector: '[data-toggle=tooltip]',
+ placement: 'top'
+ });
+ // if description for this serviceConfig not exist, then no need to show popover
+ if (this.get('isPopoverEnabled') !== 'false' && this.get('serviceConfig.description')) {
+ App.popover(this.$(), {
+ title: Em.I18n.t('installer.controls.serviceConfigPopover.title').format(
+ this.get('serviceConfig.displayName'),
+ (this.get('serviceConfig.displayName') == this.get('serviceConfig.name')) ? '' : this.get('serviceConfig.name')
+ ),
+ content: this.get('serviceConfig.description'),
+ placement: 'right',
+ trigger: 'hover'
+ });
+ }
+ },
+
+ willDestroyElement: function() {
+ this.$().popover('destroy');
+ },
+
+ readOnly: function () {
+ return !this.get('serviceConfig.isEditable');
+ }.property('serviceConfig.isEditable')
+});
+
+/**
+ * mixin set class that serve as unique element identificator,
+ * id not used in order to avoid collision with ember ids
+ */
+App.ServiceConfigCalculateId = Ember.Mixin.create({
+ idClass: Ember.computed(function () {
+ var label = Em.get(this, 'serviceConfig.name') ? Em.get(this, 'serviceConfig.name').toLowerCase().replace(/\./g, '-') : '',
+ fileName = Em.get(this, 'serviceConfig.filename') ? Em.get(this, 'serviceConfig.filename').toLowerCase().replace(/\./g, '-') : '',
+ group = Em.get(this, 'serviceConfig.group.name') || 'default';
+ isOrigin = Em.get(this, 'serviceConfig.compareConfigs.length') > 0 ? '-origin' : '';
+ return 'service-config-' + label + '-' + fileName + '-' + group + isOrigin;
+ }),
+ classNameBindings: 'idClass'
+});
+
+/**
+ * Default input control
+ * @type {*}
+ */
+App.ServiceConfigTextField = Ember.TextField.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, {
+
+ valueBinding: 'serviceConfig.value',
+ classNameBindings: 'textFieldClassName',
+ placeholderBinding: 'serviceConfig.defaultValue',
+
+ keyPress: function (event) {
+ if (event.keyCode == 13) {
+ return false;
+ }
+ },
+ //Set editDone true for last edited config text field parameter
+ focusOut: function (event) {
+ this.get('serviceConfig').set("editDone", true);
+ },
+ //Set editDone false for all current category config text field parameter
+ focusIn: function (event) {
+ if (!this.get('serviceConfig.isOverridden') && !this.get('serviceConfig.isComparison')) {
+ this.get("parentView.categoryConfigsAll").setEach("editDone", false);
+ }
+ },
+
+ textFieldClassName: function () {
+ if (this.get('serviceConfig.unit')) {
+ return ['input-small'];
+ } else if (this.get('serviceConfig.displayType') === 'principal') {
+ return ['span12'];
+ } else {
+ return ['span9'];
+ }
+ }.property('serviceConfig.displayType', 'serviceConfig.unit')
+
+});
+
+/**
+ * Customized input control with Units type specified
+ * @type {Em.View}
+ */
+App.ServiceConfigTextFieldWithUnit = Ember.View.extend(App.ServiceConfigPopoverSupport, {
+ valueBinding: 'serviceConfig.value',
+ classNames: ['input-append', 'with-unit'],
+ placeholderBinding: 'serviceConfig.defaultValue',
+
+ templateName: require('templates/wizard/controls_service_config_textfield_with_unit')
+});
+
+/**
+ * Password control
+ * @type {*}
+ */
+App.ServiceConfigPasswordField = Ember.TextField.extend({
+
+ serviceConfig: null,
+ type: 'password',
+ attributeBindings:['readOnly'],
+ valueBinding: 'serviceConfig.value',
+ classNames: [ 'span4' ],
+ placeholder: Em.I18n.t('form.item.placeholders.typePassword'),
+
+ template: Ember.Handlebars.compile('{{view view.retypePasswordView}}'),
+
+ keyPress: function (event) {
+ if (event.keyCode == 13) {
+ return false;
+ }
+ },
+
+ retypePasswordView: Ember.TextField.extend({
+ placeholder: Em.I18n.t('form.passwordRetype'),
+ attributeBindings:['readOnly'],
+ type: 'password',
+ classNames: [ 'span4', 'retyped-password' ],
+ keyPress: function (event) {
+ if (event.keyCode == 13) {
+ return false;
+ }
+ },
+ valueBinding: 'parentView.serviceConfig.retypedPassword',
+ readOnly: function () {
+ return !this.get('parentView.serviceConfig.isEditable');
+ }.property('parentView.serviceConfig.isEditable')
+ }),
+
+ readOnly: function () {
+ return !this.get('serviceConfig.isEditable');
+ }.property('serviceConfig.isEditable')
+
+});
+
+/**
+ * Textarea control
+ * @type {*}
+ */
+App.ServiceConfigTextArea = Ember.TextArea.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, {
+
+ valueBinding: 'serviceConfig.value',
+ rows: 4,
+ classNames: ['span9', 'directories']
+});
+
+/**
+ * Textarea control for content type
+ * @type {*}
+ */
+App.ServiceConfigTextAreaContent = Ember.TextArea.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, {
+
+ valueBinding: 'serviceConfig.value',
+ rows: 20,
+ classNames: ['span10']
+});
+
+/**
+ * Textarea control with bigger height
+ * @type {*}
+ */
+App.ServiceConfigBigTextArea = App.ServiceConfigTextArea.extend(App.ServiceConfigCalculateId, {
+ rows: 10
+});
+
+/**
+ * Checkbox control
+ * @type {*}
+ */
+App.ServiceConfigCheckbox = Ember.Checkbox.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, {
+
+ checkedBinding: 'serviceConfig.value',
+
+ disabled: function () {
+ return !this.get('serviceConfig.isEditable');
+ }.property('serviceConfig.isEditable')
+});
+
+App.ServiceConfigRadioButtons = Ember.View.extend(App.ServiceConfigCalculateId, {
+ templateName: require('templates/wizard/controls_service_config_radio_buttons'),
+
+ didInsertElement: function () {
+ // on page render, automatically populate JDBC URLs only for default database settings
+ // so as to not lose the user's customizations on these fields
+ if (['addServiceController', 'installerController'].contains(this.get('controller.wizardController.name'))) {
+ if (/^New\s\w+\sDatabase$/.test(this.get('serviceConfig.value'))) {
+ this.onOptionsChange();
+ } else {
+ this.handleDBConnectionProperty();
+ }
+ }
+ },
+
+ configs: function () {
+ if (this.get('controller.name') == 'mainServiceInfoConfigsController') return this.get('categoryConfigsAll');
+ return this.get('categoryConfigsAll').filterProperty('isObserved', true);
+ }.property('categoryConfigsAll'),
+
+ serviceConfig: null,
+ categoryConfigsAll: null,
+
+ onOptionsChange: function () {
+ // The following if condition will be satisfied only for installer wizard flow
+ if (this.get('configs').length) {
+ var connectionUrl = this.get('connectionUrl');
+ var dbClass = this.get('dbClass');
+ if (connectionUrl) {
+ if (this.get('serviceConfig.serviceName') === 'HIVE') {
+ var hiveDbType = this.get('parentView.serviceConfigs').findProperty('name', 'hive_database_type');
+ switch (this.get('serviceConfig.value')) {
+ case 'New MySQL Database':
+ case 'Existing MySQL Database':
+ connectionUrl.set('value', "jdbc:mysql://" + this.get('hostName') + "/" + this.get('databaseName') + "?createDatabaseIfNotExist=true");
+ dbClass.set('value', "com.mysql.jdbc.Driver");
+ Em.set(hiveDbType, 'value', 'mysql');
+ break;
+ case Em.I18n.t('services.service.config.hive.oozie.postgresql'):
+ connectionUrl.set('value', "jdbc:postgresql://" + this.get('hostName') + ":5432/" + this.get('databaseName'));
+ dbClass.set('value', "org.postgresql.Driver");
+ Em.set(hiveDbType, 'value', 'postgres');
+ break;
+ case 'Existing Oracle Database':
+ connectionUrl.set('value', "jdbc:oracle:thin:@//" + this.get('hostName') + ":1521/" + this.get('databaseName'));
+ dbClass.set('value', "oracle.jdbc.driver.OracleDriver");
+ Em.set(hiveDbType, 'value', 'oracle');
+ break;
+ case 'Existing MSSQL Server database with SQL authentication':
+ connectionUrl.set('value', "jdbc:sqlserver://" + this.get('hostName') + ";databaseName=" + this.get('databaseName'));
+ dbClass.set('value', "com.microsoft.sqlserver.jdbc.SQLServerDriver");
+ Em.set(hiveDbType, 'value', 'mssql');
+ break;
+ case 'Existing MSSQL Server database with integrated authentication':
+ connectionUrl.set('value', "jdbc:sqlserver://" + this.get('hostName') + ";databaseName=" + this.get('databaseName') + ";integratedSecurity=true");
+ dbClass.set('value', "com.microsoft.sqlserver.jdbc.SQLServerDriver");
+ Em.set(hiveDbType, 'value', 'mssql');
+ break;
+ }
+ var isNotExistingMySQLServer = this.get('serviceConfig.value') !== 'Existing MSSQL Server database with integrated authentication';
+ this.get('categoryConfigsAll').findProperty('name', 'javax.jdo.option.ConnectionUserName').setProperties({
+ isVisible: isNotExistingMySQLServer,
+ isRequired: isNotExistingMySQLServer
+ });
+ this.get('categoryConfigsAll').findProperty('name', 'javax.jdo.option.ConnectionPassword').setProperties({
+ isVisible: isNotExistingMySQLServer,
+ isRequired: isNotExistingMySQLServer
+ });
+ } else if (this.get('serviceConfig.serviceName') === 'OOZIE') {
+ switch (this.get('serviceConfig.value')) {
+ case 'New Derby Database':
+ connectionUrl.set('value', "jdbc:derby:${oozie.data.dir}/${oozie.db.schema.name}-db;create=true");
+ dbClass.set('value', "org.apache.derby.jdbc.EmbeddedDriver");
+ break;
+ case 'Existing MySQL Database':
+ connectionUrl.set('value', "jdbc:mysql://" + this.get('hostName') + "/" + this.get('databaseName'));
+ dbClass.set('value', "com.mysql.jdbc.Driver");
+ break;
+ case Em.I18n.t('services.service.config.hive.oozie.postgresql'):
+ connectionUrl.set('value', "jdbc:postgresql://" + this.get('hostName') + ":5432/" + this.get('databaseName'));
+ dbClass.set('value', "org.postgresql.Driver");
+ break;
+ case 'Existing Oracle Database':
+ connectionUrl.set('value', "jdbc:oracle:thin:@//" + this.get('hostName') + ":1521/" + this.get('databaseName'));
+ dbClass.set('value', "oracle.jdbc.driver.OracleDriver");
+ break;
+ case 'Existing MSSQL Server database with SQL authentication':
+ connectionUrl.set('value', "jdbc:sqlserver://" + this.get('hostName') + ";databaseName=" + this.get('databaseName'));
+ dbClass.set('value', "com.microsoft.sqlserver.jdbc.SQLServerDriver");
+ break;
+ case 'Existing MSSQL Server database with integrated authentication':
+ connectionUrl.set('value', "jdbc:sqlserver://" + this.get('hostName') + ";databaseName=" + this.get('databaseName') + ";integratedSecurity=true");
+ dbClass.set('value', "com.microsoft.sqlserver.jdbc.SQLServerDriver");
+ break;
+ }
+ isNotExistingMySQLServer = this.get('serviceConfig.value') !== 'Existing MSSQL Server database with integrated authentication';
+ this.get('categoryConfigsAll').findProperty('name', 'oozie.service.JPAService.jdbc.username').setProperties({
+ isVisible: isNotExistingMySQLServer,
+ isRequired: isNotExistingMySQLServer
+ });
+ this.get('categoryConfigsAll').findProperty('name', 'oozie.service.JPAService.jdbc.password').setProperties({
+ isVisible: isNotExistingMySQLServer,
+ isRequired: isNotExistingMySQLServer
+ });
+ }
+ connectionUrl.set('defaultValue', connectionUrl.get('value'));
+ }
+ }
+ }.observes('databaseName', 'hostName'),
+
+ nameBinding: 'serviceConfig.radioName',
+
+ databaseNameProperty: function () {
+ switch (this.get('serviceConfig.serviceName')) {
+ case 'HIVE':
+ return this.get('categoryConfigsAll').findProperty('name', 'ambari.hive.db.schema.name');
+ case 'OOZIE':
+ return this.get('categoryConfigsAll').findProperty('name', 'oozie.db.schema.name');
+ default:
+ return null;
+ }
+ }.property('serviceConfig.serviceName'),
+
+ databaseName: function () {
+ return this.get('databaseNameProperty.value');
+ }.property('databaseNameProperty.value'),
+
+ hostNameProperty: function () {
+ var value = this.get('serviceConfig.value');
+ var returnValue;
+ var hostname;
+ if (this.get('serviceConfig.serviceName') === 'HIVE') {
+ switch (value) {
+ case 'New MySQL Database':
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'hive_ambari_host');
+ break;
+ case 'Existing MySQL Database':
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'hive_existing_mysql_host');
+ break;
+ case Em.I18n.t('services.service.config.hive.oozie.postgresql'):
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'hive_existing_postgresql_host');
+ break;
+ case 'Existing Oracle Database':
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'hive_existing_oracle_host');
+ break;
+ case 'Existing MSSQL Server database with SQL authentication':
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'hive_existing_mssql_server_host');
+ break;
+ case 'Existing MSSQL Server database with integrated authentication':
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'hive_existing_mssql_server_2_host');
+ break;
+ }
+ if (hostname) {
+ returnValue = hostname;
+ } else {
+ returnValue = this.get('categoryConfigsAll').findProperty('name', 'hive_hostname');
+ }
+ } else if (this.get('serviceConfig.serviceName') === 'OOZIE') {
+ switch (value) {
+ case 'New Derby Database':
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'oozie_ambari_host');
+ break;
+ case 'Existing MySQL Database':
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'oozie_existing_mysql_host');
+ break;
+ case Em.I18n.t('services.service.config.hive.oozie.postgresql'):
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'oozie_existing_postgresql_host');
+ break;
+ case 'Existing Oracle Database':
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'oozie_existing_oracle_host');
+ break;
+ case 'Existing MSSQL Server database with SQL authentication':
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'oozie_existing_mssql_server_host');
+ break;
+ case 'Existing MSSQL Server database with integrated authentication':
+ hostname = this.get('categoryConfigsAll').findProperty('name', 'oozie_existing_mssql_server_2_host');
+ break;
+ }
+ if (hostname) {
+ returnValue = hostname;
+ } else {
+ returnValue = this.get('categoryConfigsAll').findProperty('name', 'oozie_hostname');
+ }
+ }
+ return returnValue;
+ }.property('serviceConfig.serviceName', 'serviceConfig.value'),
+
+ hostName: function () {
+ return this.get('hostNameProperty.value');
+ }.property('hostNameProperty.value'),
+
+ connectionUrl: function () {
+ if (this.get('serviceConfig.serviceName') === 'HIVE') {
+ return this.get('categoryConfigsAll').findProperty('name', 'javax.jdo.option.ConnectionURL');
+ } else {
+ return this.get('categoryConfigsAll').findProperty('name', 'oozie.service.JPAService.jdbc.url');
+ }
+ }.property('serviceConfig.serviceName'),
+
+ dbClass: function () {
+ if (this.get('serviceConfig.serviceName') === 'HIVE') {
+ return this.get('categoryConfigsAll').findProperty('name', 'javax.jdo.option.ConnectionDriverName');
+ } else {
+ return this.get('categoryConfigsAll').findProperty('name', 'oozie.service.JPAService.jdbc.driver');
+ }
+ }.property('serviceConfig.serviceName'),
+
+ /**
+ * `Observer` that add <code>additionalView</code> to <code>App.ServiceConfigProperty</code>
+ * that responsible for (if existing db selected)
+ * 1. checking database connection
+ * 2. showing jdbc driver setup warning msg.
+ *
+ * @method handleDBConnectionProperty
+ **/
+ handleDBConnectionProperty: function() {
+ var handledProperties = ['oozie_database', 'hive_database'];
+ var currentValue = this.get('serviceConfig.value');
+ var databases = /MySQL|PostgreSQL|Oracle|Derby|MSSQL/gi;
+ var currentDB = currentValue.match(databases)[0];
+ var databasesTypes = /MySQL|PostgreS|Oracle|Derby|MSSQL/gi;
+ var currentDBType = currentValue.match(databasesTypes)[0];
+ var existingDatabase = /existing/gi.test(currentValue);
+ // db connection check button show up if existed db selected
+ var propertyAppendTo1 = this.get('categoryConfigsAll').findProperty('displayName', 'Database URL');
+ if (currentDB && existingDatabase) {
+ if (handledProperties.contains(this.get('serviceConfig.name'))) {
+ if (propertyAppendTo1) propertyAppendTo1.set('additionalView', App.CheckDBConnectionView.extend({databaseName: currentDB}));
+ }
+ } else {
+ propertyAppendTo1.set('additionalView', null);
+ }
+ // warning msg under database type radio buttons, to warn the user to setup jdbc driver if existed db selected
+ var propertyHive = this.get('categoryConfigsAll').findProperty('displayName', 'Hive Database');
+ var propertyOozie = this.get('categoryConfigsAll').findProperty('displayName', 'Oozie Database');
+ var propertyAppendTo2 = propertyHive ? propertyHive : propertyOozie;
+ if (currentDB && existingDatabase) {
+ if (handledProperties.contains(this.get('serviceConfig.name'))) {
+ if (propertyAppendTo2) {
+ propertyAppendTo2.set('additionalView', Ember.View.extend({
+ template: Ember.Handlebars.compile('<div class="alert">{{{view.message}}}</div>'),
+ message: Em.I18n.t('services.service.config.database.msg.jdbcSetup').format(currentDBType.toLowerCase(), currentDBType.toLowerCase())
+ }));
+ }
+ }
+ } else {
+ propertyAppendTo2.set('additionalView', null);
+ }
+ }.observes('serviceConfig.value'),
+
+ optionsBinding: 'serviceConfig.options'
+});
+
+App.ServiceConfigRadioButton = Ember.Checkbox.extend({
+ tagName: 'input',
+ attributeBindings: ['type', 'name', 'value', 'checked', 'disabled'],
+ checked: false,
+ type: 'radio',
+ name: null,
+ value: null,
+
+ didInsertElement: function () {
+ console.debug('App.ServiceConfigRadioButton.didInsertElement');
+ if (this.get('parentView.serviceConfig.value') === this.get('value')) {
+ console.debug(this.get('name') + ":" + this.get('value') + ' is checked');
+ this.set('checked', true);
+ }
+ },
+
+ click: function () {
+ this.set('checked', true);
+ console.debug('App.ServiceConfigRadioButton.click');
+ this.onChecked();
+ },
+
+ onChecked: function () {
+ // Wrapping the call with Ember.run.next prevents a problem where setting isVisible on component
+ // causes JS error due to re-rendering. For example, this occurs when switching the Config Group
+ // in Service Config page
+ Em.run.next(this, function() {
+ console.debug('App.ServiceConfigRadioButton.onChecked');
+ this.set('parentView.serviceConfig.value', this.get('value'));
+ var components = this.get('parentView.serviceConfig.options');
+ if (components) {
+ components.forEach(function (_component) {
+ if (_component.foreignKeys) {
+ _component.foreignKeys.forEach(function (_componentName) {
+ if (this.get('parentView.categoryConfigsAll').someProperty('name', _componentName)) {
+ var component = this.get('parentView.categoryConfigsAll').findProperty('name', _componentName);
+ component.set('isVisible', _component.displayName === this.get('value'));
+ }
+ }, this);
+ }
+ }, this);
+ }
+ });
+ }.observes('checked'),
+
+ disabled: function () {
+ return !this.get('parentView.serviceConfig.isEditable') ||
+ !['addServiceController', 'installerController'].contains(this.get('controller.wizardController.name')) && /^New\s\w+\sDatabase$/.test(this.get('value'));
+ }.property('parentView.serviceConfig.isEditable')
+});
+
+App.ServiceConfigComboBox = Ember.Select.extend(App.ServiceConfigPopoverSupport, App.ServiceConfigCalculateId, {
+ contentBinding: 'serviceConfig.options',
+ selectionBinding: 'serviceConfig.value',
+ placeholderBinding: 'serviceConfig.defaultValue',
+ classNames: [ 'span3' ]
+});
+
+
+/**
+ * Base component for host config with popover support
+ */
+App.ServiceConfigHostPopoverSupport = Ember.Mixin.create({
+
+ /**
+ * Config object. It will instance of App.ServiceConfigProperty
+ */
+ serviceConfig: null,
+
+ didInsertElement: function () {
+ App.popover(this.$(), {
+ title: this.get('serviceConfig.displayName'),
+ content: this.get('serviceConfig.description'),
+ placement: 'right',
+ trigger: 'hover'
+ });
+ }
+});
+
+/**
+ * Master host component.
+ * Show hostname without ability to edit it
+ * @type {*}
+ */
+App.ServiceConfigMasterHostView = Ember.View.extend(App.ServiceConfigHostPopoverSupport, App.ServiceConfigCalculateId, {
+
+ classNames: ['master-host', 'span6'],
+ valueBinding: 'serviceConfig.value',
+
+ template: Ember.Handlebars.compile('{{value}}')
+
+});
+
+/**
+ * text field property view that enables possibility
+ * for check connectio
+ * @type {*}
+ */
+App.checkConnectionView = App.ServiceConfigTextField.extend({
+ didInsertElement: function() {
+ this._super();
+ var kdc = this.get('categoryConfigsAll').findProperty('name', 'kdc_type');
+ var propertyAppendTo = this.get('categoryConfigsAll').findProperty('name', 'admin_password');
+ if (propertyAppendTo) propertyAppendTo.set('additionalView', App.CheckDBConnectionView.extend({databaseName: kdc && kdc.get('value')}));
+ }
+});
+
+/**
+ * Show value as plain label in italics
+ * @type {*}
+ */
+App.ServiceConfigLabelView = Ember.View.extend(App.ServiceConfigHostPopoverSupport, App.ServiceConfigCalculateId, {
+
+ classNames: ['master-host', 'span6'],
+ valueBinding: 'serviceConfig.value',
+
+ template: Ember.Handlebars.compile('<i>{{view.value}}</i>')
+});
+
+/**
+ * Base component to display Multiple hosts
+ * @type {*}
+ */
+App.ServiceConfigMultipleHostsDisplay = Ember.Mixin.create(App.ServiceConfigHostPopoverSupport, App.ServiceConfigCalculateId, {
+
+ hasNoHosts: function () {
+ console.log('view', this.get('viewName')); //to know which View cause errors
+ console.log('controller', this.get('controller').name); //should be slaveComponentGroupsController
+ if (!this.get('value')) {
+ return true;
+ }
+ return this.get('value').length === 0;
+ }.property('value'),
+
+ hasOneHost: function () {
+ return this.get('value').length === 1;
+ }.property('value'),
+
+ hasMultipleHosts: function () {
+ return this.get('value').length > 1;
+ }.property('value'),
+
+ otherLength: function () {
+ var len = this.get('value').length;
+ if (len > 2) {
+ return Em.I18n.t('installer.controls.serviceConfigMultipleHosts.others').format(len - 1);
+ } else {
+ return Em.I18n.t('installer.controls.serviceConfigMultipleHosts.other');
+ }
+ }.property('value')
+
+});
+
+
+/**
+ * Multiple master host component.
+ * Show hostnames without ability to edit it
+ * @type {*}
+ */
+App.ServiceConfigMasterHostsView = Ember.View.extend(App.ServiceConfigMultipleHostsDisplay, App.ServiceConfigCalculateId, {
+
+ viewName: "serviceConfigMasterHostsView",
+ valueBinding: 'serviceConfig.value',
+
+ classNames: ['master-hosts', 'span6'],
+ templateName: require('templates/wizard/master_hosts'),
+
+ /**
+ * Onclick handler for link
+ */
+ showHosts: function () {
+ var serviceConfig = this.get('serviceConfig');
+ App.ModalPopup.show({
+ header: Em.I18n.t('installer.controls.serviceConfigMasterHosts.header').format(serviceConfig.category),
+ bodyClass: Ember.View.extend({
+ serviceConfig: serviceConfig,
+ templateName: require('templates/wizard/master_hosts_popup')
+ }),
+ secondary: null
+ });
+ }
+
+});
+
+/**
+ * Show tabs list for slave hosts
+ * @type {*}
+ */
+App.SlaveComponentGroupsMenu = Em.CollectionView.extend(App.ServiceConfigCalculateId, {
+
+ content: function () {
+ return this.get('controller.componentGroups');
+ }.property('controller.componentGroups'),
+
+ tagName: 'ul',
+ classNames: ["nav", "nav-tabs"],
+
+ itemViewClass: Em.View.extend({
+ classNameBindings: ["active"],
+
+ active: function () {
+ return this.get('content.active');
+ }.property('content.active'),
+
+ errorCount: function () {
+ return this.get('content.properties').filterProperty('isValid', false).filterProperty('isVisible', true).get('length');
+ }.property('content.properties.@each.isValid', 'content.properties.@each.isVisible'),
+
+ templateName: require('templates/wizard/controls_slave_component_groups_menu')
+ })
+
+});
+
+/**
+ * <code>Add group</code> button
+ * @type {*}
+ */
+App.AddSlaveComponentGroupButton = Ember.View.extend(App.ServiceConfigCalculateId, {
+
+ tagName: 'span',
+ slaveComponentName: null,
+
+ didInsertElement: function () {
+ App.popover(this.$(), {
+ title: Em.I18n.t('installer.controls.addSlaveComponentGroupButton.title').format(this.get('slaveComponentName')),
+ content: Em.I18n.t('installer.controls.addSlaveComponentGroupButton.content').format(this.get('slaveComponentName'), this.get('slaveComponentName'), this.get('slaveComponentName')),
+ placement: 'right',
+ trigger: 'hover'
+ });
+ }
+
+});
+
+/**
+ * Multiple Slave Hosts component
+ * @type {*}
+ */
+App.ServiceConfigSlaveHostsView = Ember.View.extend(App.ServiceConfigMultipleHostsDisplay, App.ServiceConfigCalculateId, {
+
+ viewName: 'serviceConfigSlaveHostsView',
+
+ classNames: ['slave-hosts', 'span6'],
+
+ valueBinding: 'serviceConfig.value',
+
+ templateName: require('templates/wizard/slave_hosts'),
+
+ /**
+ * Onclick handler for link
+ */
+ showHosts: function () {
+ var serviceConfig = this.get('serviceConfig');
+ App.ModalPopup.show({
+ header: Em.I18n.t('installer.controls.serviceConfigMasterHosts.header').format(serviceConfig.category),
+ bodyClass: Ember.View.extend({
+ serviceConfig: serviceConfig,
+ templateName: require('templates/wizard/master_hosts_popup')
+ }),
+ secondary: null
+ });
+ }
+
+});
+
+/**
+ * properties for present active slave group
+ * @type {*}
+ */
+App.SlaveGroupPropertiesView = Ember.View.extend(App.ServiceConfigCalculateId, {
+
+ viewName: 'serviceConfigSlaveHostsView',
+
+ group: function () {
+ return this.get('controller.activeGroup');
+ }.property('controller.activeGroup'),
+
+ groupConfigs: function () {
+ console.log("************************************************************************");
+ console.log("The value of group is: " + this.get('group'));
+ console.log("************************************************************************");
+ return this.get('group.properties');
+ }.property('group.properties.@each').cacheable(),
+
+ errorCount: function () {
+ return this.get('group.properties').filterProperty('isValid', false).filterProperty('isVisible', true).get('length');
+ }.property('configs.@each.isValid', 'configs.@each.isVisible')
+});
+
+/**
+ * DropDown component for <code>select hosts for groups</code> popup
+ * @type {*}
+ */
+App.SlaveComponentDropDownGroupView = Ember.View.extend(App.ServiceConfigCalculateId, {
+
+ viewName: "slaveComponentDropDownGroupView",
+
+ /**
+ * On change handler for <code>select hosts for groups</code> popup
+ * @param event
+ */
+ changeGroup: function (event) {
+ var host = this.get('content');
+ var groupName = $('#' + this.get('elementId') + ' select').val();
+ this.get('controller').changeHostGroup(host, groupName);
+ },
+
+ optionTag: Ember.View.extend({
+
+ /**
+ * Whether current value(OptionTag value) equals to host value(assigned to SlaveComponentDropDownGroupView.content)
+ */
+ selected: function () {
+ return this.get('parentView.content.group') === this.get('content');
+ }.property('content')
+ })
+});
+
+/**
+ * Show info about current group
+ * @type {*}
+ */
+App.SlaveComponentChangeGroupNameView = Ember.View.extend(App.ServiceConfigCalculateId, {
+
+ contentBinding: 'controller.activeGroup',
+ classNames: ['control-group'],
+ classNameBindings: 'error',
+ error: false,
+ setError: function () {
+ this.set('error', false);
+ }.observes('controller.activeGroup'),
+ errorMessage: function () {
+ return this.get('error') ? Em.I18n.t('installer.controls.slaveComponentChangeGroupName.error') : '';
+ }.property('error'),
+
+ /**
+ * Onclick handler for saving updated group name
+ * @param event
+ */
+ changeGroupName: function (event) {
+ var inputVal = $('#' + this.get('elementId') + ' input[type="text"]').val();
+ if (inputVal !== this.get('content.name')) {
+ var result = this.get('controller').changeSlaveGroupName(this.get('content'), inputVal);
+ this.set('error', result);
+ }
+ }
+});
+/**
+ * View for testing connection to database.
+ **/
+App.CheckDBConnectionView = Ember.View.extend({
+ templateName: require('templates/common/form/check_db_connection'),
+ /** @property {string} btnCaption - text for button **/
+ btnCaption: Em.I18n.t('services.service.config.database.btn.idle'),
+ /** @property {string} responseCaption - text for status link **/
+ responseCaption: null,
+ /** @property {boolean} isConnecting - is request to server activated **/
+ isConnecting: false,
+ /** @property {boolean} isValidationPassed - check validation for required fields **/
+ isValidationPassed: null,
+ /** @property {string} databaseName- name of current database **/
+ databaseName: null,
+ /** @property {boolean} isRequestResolved - check for finished request to server **/
+ isRequestResolved: false,
+ /** @property {boolean} isConnectionSuccess - check for successful connection to database **/
+ isConnectionSuccess: null,
+ /** @property {string} responseFromServer - message from server response **/
+ responseFromServer: null,
+ /** @property {Object} ambariRequiredProperties - properties that need for custom action request **/
+ ambariRequiredProperties: null,
+ /** @property {Number} currentRequestId - current custom action request id **/
+ currentRequestId: null,
+ /** @property {Number} currentTaskId - current custom action task id **/
+ currentTaskId: null,
+ /** @property {jQuery.Deferred} request - current $.ajax request **/
+ request: null,
+ /** @property {Number} pollInterval - timeout interval for ajax polling **/
+ pollInterval: 3000,
+ /** @property {string} hostNameProperty - host name property based on service and database names **/
+ hostNameProperty: function() {
+ if (!/wizard/i.test(this.get('controller.name')) && this.get('parentView.service.serviceName') === 'HIVE') {
+ return this.get('parentView.service.serviceName').toLowerCase() + '_hostname';
+ } else if (this.get('parentView.service.serviceName') === 'KERBEROS') {
+ return 'kdc_host';
+ }
+ return '{0}_existing_{1}_host'.format(this.get('parentView.service.serviceName').toLowerCase(), this.get('databaseName').toLowerCase());
+ }.property('databaseName'),
+ /** @property {boolean} isBtnDisabled - disable button on failed validation or active request **/
+ isBtnDisabled: function() {
+ return !this.get('isValidationPassed') || this.get('isConnecting');
+ }.property('isValidationPassed', 'isConnecting'),
+ /** @property {object} requiredProperties - properties that necessary for database connection **/
+ requiredProperties: function() {
+ var propertiesMap = {
+ OOZIE: ['oozie.db.schema.name','oozie.service.JPAService.jdbc.username','oozie.service.JPAService.jdbc.password','oozie.service.JPAService.jdbc.driver','oozie.service.JPAService.jdbc.url'],
+ HIVE: ['ambari.hive.db.schema.name','javax.jdo.option.ConnectionUserName','javax.jdo.option.ConnectionPassword','javax.jdo.option.ConnectionDriverName','javax.jdo.option.ConnectionURL'],
+ KERBEROS: ['kdc_host']
+ };
+ return propertiesMap[this.get('parentView.service.serviceName')];
+ }.property(),
+ /** @property {Object} propertiesPattern - check pattern according to type of connection properties **/
+ propertiesPattern: function() {
+ var patterns = {
+ db_connection_url: /jdbc\.url|connectionurl|kdc_host/ig
+ };
+ if (this.get('parentView.service.serviceName') != "KERBEROS") {
+ patterns.user_name = /(username|dblogin)$/ig;
+ patterns.user_passwd = /(dbpassword|password)$/ig;
+ }
+ return patterns;
+ }.property('parentView.service.serviceName'),
+ /** @property {String} masterHostName - host name location of Master Component related to Service **/
+ masterHostName: function() {
+ var serviceMasterMap = {
+ 'OOZIE': 'oozieserver_host',
+ 'HDFS': 'hadoop_host',
+ 'HIVE': 'hive_ambari_host',
+ 'KERBEROS': 'kdc_host'
+ };
+ return this.get('parentView.categoryConfigsAll').findProperty('name', serviceMasterMap[this.get('parentView.service.serviceName')]).get('value');
+ }.property('parentView.service.serviceName', 'parentView.categoryConfigsAll.@each.value'),
+ /** @property {Object} connectionProperties - service specific config values mapped for custom action request **/
+ connectionProperties: function() {
+ var propObj = {};
+ for (var key in this.get('propertiesPattern')) {
+ propObj[key] = this.getConnectionProperty(this.get('propertiesPattern')[key]);
+ }
+ return propObj;
+ }.property('parentView.categoryConfigsAll.@each.value'),
+ /**
+ * Properties that stores in local storage used for handling
+ * last success connection.
+ *
+ * @property {Object} preparedDBProperties
+ **/
+ preparedDBProperties: function() {
+ var propObj = {};
+ for (var key in this.get('propertiesPattern')) {
+ var propName = this.getConnectionProperty(this.get('propertiesPattern')[key], true);
+ propObj[propName] = this.get('parentView.categoryConfigsAll').findProperty('name', propName).get('value');
+ }
+ return propObj;
+ }.property(),
+ /** Check validation and load ambari properties **/
+ didInsertElement: function() {
+ var kdc = this.get('parentView.categoryConfigsAll').findProperty('name', 'kdc_type');
+ if (kdc) {
+ var name = kdc.get('value') == 'Existing MIT KDC' ? 'KDC' : 'AD';
+ App.popover(this.$(), {
+ title: Em.I18n.t('services.service.config.database.btn.idle'),
+ content: Em.I18n.t('installer.controls.checkConnection.popover').format(name),
+ placement: 'right',
+ trigger: 'hover'
+ });
+ }
+ this.handlePropertiesValidation();
+ this.getAmbariProperties();
+ },
+ /** On view destroy **/
+ willDestroyElement: function() {
+ this.set('isConnecting', false);
+ this._super();
+ },
+ /**
+ * Observer that take care about enabling/disabling button based on required properties validation.
+ *
+ * @method handlePropertiesValidation
+ **/
+ handlePropertiesValidation: function() {
+ this.restore();
+ var isValid = true;
+ var properties = [].concat(this.get('requiredProperties'));
+ properties.push(this.get('hostNameProperty'));
+ properties.forEach(function(propertyName) {
+ var property = this.get('parentView.categoryConfigsAll').findProperty('name', propertyName);
+ if(property && !property.get('isValid')) isValid = false;
+ }, this);
+ this.set('isValidationPassed', isValid);
+ }.observes('parentView.categoryConfigsAll.@each.isValid', 'parentView.categoryConfigsAll.@each.value', 'databaseName'),
+
+ getConnectionProperty: function(regexp, isGetName) {
+ var _this = this;
+ var propertyName = _this.get('requiredProperties').filter(function(item) {
+ return regexp.test(item);
+ })[0];
+ return (isGetName) ? propertyName : _this.get('parentView.categoryConfigsAll').findProperty('name', propertyName).get('value');
+ },
+ /**
+ * Set up ambari properties required for custom action request
+ *
+ * @method getAmbariProperties
+ **/
+ getAmbariProperties: function() {
+ var clusterController = App.router.get('clusterController');
+ var _this = this;
+ if (!App.isEmptyObject(App.db.get('tmp', 'ambariProperties')) && !this.get('ambariProperties')) {
+ this.set('ambariProperties', App.db.get('tmp', 'ambariProperties'));
+ return;
+ }
+ if (App.isEmptyObject(clusterController.get('ambariProperties'))) {
+ clusterController.loadAmbariProperties().done(function(data) {
+ _this.formatAmbariProperties(data.RootServiceComponents.properties);
+ });
+ } else {
+ this.formatAmbariProperties(clusterController.get('ambariProperties'));
+ }
+ },
+
+ formatAmbariProperties: function(properties) {
+ var defaults = {
+ threshold: "60",
+ ambari_server_host: location.hostname,
+ check_execute_list : "db_connection_check"
+ };
+ var properties = App.permit(properties, ['jdk.name','jdk_location','java.home']);
+ var renameKey = function(oldKey, newKey) {
+ if (properties[oldKey]) {
+ defaults[newKey] = properties[oldKey];
+ delete properties[oldKey];
+ }
+ };
+ renameKey('java.home', 'java_home');
+ renameKey('jdk.name', 'jdk_name');
+ $.extend(properties, defaults);
+ App.db.set('tmp', 'ambariProperties', properties);
+ this.set('ambariProperties', properties);
+ },
+ /**
+ * `Action` method for starting connect to current database.
+ *
+ * @method connectToDatabase
+ **/
+ connectToDatabase: function() {
+ if (this.get('isBtnDisabled')) return;
+ this.set('isRequestResolved', false);
+ App.db.set('tmp', this.get('parentView.service.serviceName') + '_connection', {});
+ this.setConnectingStatus(true);
+ if (App.get('testMode')) {
+ this.startPolling();
+ } else {
+ this.runCheckConnection();
+ }
+ },
+
+ /**
+ * runs check connections methods depending on service
+ * @return {void}
+ * @method runCheckConnection
+ */
+ runCheckConnection: function() {
+ if (this.get('parentView.service.serviceName') === 'KERBEROS') {
+ this.runKDCCheck();
+ } else {
+ this.createCustomAction();
+ }
+ },
+
+ /**
+ * send ajax request to perforn kdc host check
+ * @return {App.ajax}
+ * @method runKDCCheck
+ */
+ runKDCCheck: function() {
+ return App.ajax.send({
+ name: 'admin.kerberos_security.test_connection',
+ sender: this,
+ data: {
+ kdcHostname: this.get('masterHostName')
+ },
+ success: 'onRunKDCCheckSuccess',
+ error: 'onCreateActionError'
+ });
+ },
+
+ /**
+ *
+ * @param data
+ */
+ onRunKDCCheckSuccess: function(data) {
+ var statusCode = {
+ success: 'REACHABLE',
+ failed: 'UNREACHABLE'
+ };
+ if (data == statusCode['success']) {
+ this.setResponseStatus('success');
+ } else {
+ this.setResponseStatus('failed');
+ }
+ this.set('responseFromServer', data);
+ },
+
+ /**
+ * Run custom action for database connection.
+ *
+ * @method createCustomAction
+ **/
+ createCustomAction: function() {
+ var dbName = this.get('databaseName').toLowerCase() === 'postgresql' ? 'postgres' : this.get('databaseName').toLowerCase();
+ var params = $.extend(true, {}, { db_name: dbName }, this.get('connectionProperties'), this.get('ambariProperties'));
+ App.ajax.send({
+ name: 'custom_action.create',
+ sender: this,
+ data: {
+ requestInfo: {
+ parameters: params
+ },
+ filteredHosts: [this.get('masterHostName')]
+ },
+ success: 'onCreateActionSuccess',
+ error: 'onCreateActionError'
+ });
+ },
+ /**
+ * Run updater if task is created successfully.
+ *
+ * @method onConnectActionS
+ **/
+ onCreateActionSuccess: function(data) {
+ this.set('currentRequestId', data.Requests.id);
+ App.ajax.send({
+ name: 'custom_action.request',
+ sender: this,
+ data: {
+ requestId: this.get('currentRequestId')
+ },
+ success: 'setCurrentTaskId'
+ });
+ },
+
+ setCurrentTaskId: function(data) {
+ this.set('currentTaskId', data.items[0].Tasks.id);
+ this.startPolling();
+ },
+
+ startPolling: function() {
+ if (this.get('isConnecting'))
+ this.getTaskInfo();
+ },
+
+ getTaskInfo: function() {
+ var request = App.ajax.send({
+ name: 'custom_action.request',
+ sender: this,
+ data: {
+ requestId: this.get('currentRequestId'),
+ taskId: this.get('currentTaskId')
+ },
+ success: 'getTaskInfoSuccess'
+ });
+ this.set('request', request);
+ },
+
+ getTaskInfoSuccess: function(data) {
+ var task = data.Tasks;
+ this.set('responseFromServer', {
+ stderr: task.stderr,
+ stdout: task.stdout
+ });
+ if (task.status === 'COMPLETED') {
+ var structuredOut = task.structured_out.db_connection_check;
+ if (structuredOut.exit_code != 0) {
+ this.set('responseFromServer', {
+ stderr: task.stderr,
+ stdout: task.stdout,
+ structuredOut: structuredOut.message
+ });
+ this.setResponseStatus('failed');
+ } else {
+ App.db.set('tmp', this.get('parentView.service.serviceName') + '_connection', this.get('preparedDBProperties'));
+ this.setResponseStatus('success');
+ }
+ }
+ if (task.status === 'FAILED') {
+ this.setResponseStatus('failed');
+ }
+ if (/PENDING|QUEUED|IN_PROGRESS/.test(task.status)) {
+ Em.run.later(this, function() {
+ this.startPolling();
+ }, this.get('pollInterval'));
+ }
+ },
+
+ onCreateActionError: function(jqXhr, status, errorMessage) {
+ this.setResponseStatus('failed');
+ this.set('responseFromServer', errorMessage);
+ },
+
+ setResponseStatus: function(isSuccess) {
+ var isSuccess = isSuccess == 'success';
+ this.setConnectingStatus(false);
+ this.set('responseCaption', isSuccess ? Em.I18n.t('services.service.config.database.connection.success') : Em.I18n.t('services.service.config.database.connection.failed'));
+ this.set('isConnectionSuccess', isSuccess);
+ this.set('isRequestResolved', true);
+ },
+ /**
+ * Switch captions and statuses for active/non-active request.
+ *
+ * @method setConnectionStatus
+ * @param {Boolean} [active]
+ */
+ setConnectingStatus: function(active) {
+ if (active) {
+ this.set('responseCaption', Em.I18n.t('services.service.config.database.connection.inProgress'));
+ }
+ this.set('controller.testConnectionInProgress', !!active);
+ this.set('btnCaption', !!active ? Em.I18n.t('services.service.config.database.btn.connecting') : Em.I18n.t('services.service.config.database.btn.idle'));
+ this.set('isConnecting', !!active);
+ },
+ /**
+ * Set view to init status.
+ *
+ * @method restore
+ **/
+ restore: function() {
+ if (this.get('request')) {
+ this.get('request').abort();
+ this.set('request', null);
+ }
+ this.set('responseCaption', null);
+ this.set('responseFromServer', null);
+ this.setConnectingStatus(false);
+ this.set('isRequestResolved', false);
+ },
+ /**
+ * `Action` method for showing response from server in popup.
+ *
+ * @method showLogsPopup
+ **/
+ showLogsPopup: function() {
+ if (this.get('isConnectionSuccess')) return;
+ var _this = this;
+ var popup = App.showAlertPopup('Error: {0} connection'.format(this.get('databaseName')));
+ if (typeof this.get('responseFromServer') == 'object') {
+ popup.set('bodyClass', Em.View.extend({
+ templateName: require('templates/common/error_log_body'),
+ openedTask: _this.get('responseFromServer')
+ }));
+ } else {
+ popup.set('body', this.get('responseFromServer'));
+ }
+ return popup;
+ }
+});
+
+/**
+ *
+ * @type {*}
+ */
+App.BaseUrlTextField = Ember.TextField.extend({
+
+ valueBinding: 'repository.baseUrl',
+
+ keyUp: function (event) {
+ if (Em.get(this, 'repository.hasError')) {
+ Em.set(this, 'repository.hasError', false);
+ }
+ }
+
+});
+
http://git-wip-us.apache.org/repos/asf/ambari/blob/11efa2f5/ambari-web/app/views/main/admin/stack_upgrade/upgrade_version_box_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_version_box_view.js b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_version_box_view.js
index cce4790..505c3bb 100644
--- a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_version_box_view.js
+++ b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_version_box_view.js
@@ -129,6 +129,7 @@ App.UpgradeVersionBoxView = Em.View.extend({
var repoRecord = App.RepositoryVersion.find(this.get('content.id'));
//make deep copy of repoRecord
var repo = Em.Object.create({
+ repoVersionId: repoRecord.get('id'),
displayName: repoRecord.get('displayName'),
repositoryVersion: repoRecord.get('displayName'),
operatingSystems: repoRecord.get('operatingSystems').map(function (os) {
@@ -140,7 +141,8 @@ App.UpgradeVersionBoxView = Em.View.extend({
return Em.Object.create({
repoName: repository.get('repoName'),
repoId: repository.get('repoId'),
- baseUrl: repository.get('baseUrl')
+ baseUrl: repository.get('baseUrl'),
+ hasError: false
});
})
});
@@ -149,20 +151,48 @@ App.UpgradeVersionBoxView = Em.View.extend({
return App.ModalPopup.show({
classNames: ['repository-list', 'sixty-percent-width-modal'],
+ skipValidation: false,
+ hasErrors: false,
bodyClass: Ember.View.extend({
content: repo,
+ skipCheckBox: Ember.Checkbox.extend({
+ classNames: ["align-checkbox"],
+ change: function() {
+ this.get('parentView.content.operatingSystems').forEach(function(os) {
+ if (Em.get(os, 'repositories.length') > 0) {
+ os.get('repositories').forEach(function(repo) {
+ Em.set(repo, 'hasError', false);
+ })
+ }
+ });
+ }
+ }),
templateName: require('templates/main/admin/stack_upgrade/edit_repositories'),
- skipValidation: false,
didInsertElement: function () {
App.tooltip($("[rel=skip-validation-tooltip]"), {placement: 'right'});
- }
+ },
+ OSCheckBox: Ember.Checkbox.extend({
+ classNames: ["align-checkbox"],
+
+ checkedBinding: "os.isSelected",
+
+ change: function () {
+ this.get('os.repositories').setEach('hasError', false);
+ }
+ })
}),
header: Em.I18n.t('common.repositories'),
primary: Em.I18n.t('common.save'),
disablePrimary: !(App.get('isAdmin') && !App.get('isOperator')),
onPrimary: function () {
- this.hide();
- self.get('controller').saveRepoOS();
+ var self = this;
+ App.get('router.mainAdminStackAndUpgradeController').saveRepoOS(repo, this.get('skipValidation')).done(function(data){
+ if (data.length > 0) {
+ self.set('hasErrors', true);
+ } else {
+ self.hide();
+ }
+ })
}
});
},