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"}}&nbsp;{{os.osType}}
+      {{view view.OSCheckBox osBinding="os"}}&nbsp;{{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();
+          }
+        })
       }
     });
   },