You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2015/09/22 19:26:35 UTC

ambari git commit: Revert "AMBARI-13187. Refactor host components utils (onechiporenko)" Reverting due to rat check failure This reverts commit 0fc88bed2dd3829cf5c33885c92018cec9a788e6.

Repository: ambari
Updated Branches:
  refs/heads/trunk e159406c4 -> 2ecda9da7


Revert "AMBARI-13187. Refactor host components utils (onechiporenko)"
Reverting due to rat check failure
This reverts commit 0fc88bed2dd3829cf5c33885c92018cec9a788e6.


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/2ecda9da
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/2ecda9da
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/2ecda9da

Branch: refs/heads/trunk
Commit: 2ecda9da7f4d3d6c8496ecd106e277adeace8082
Parents: e159406
Author: Yusaku Sako <yu...@hortonworks.com>
Authored: Tue Sep 22 10:25:57 2015 -0700
Committer: Yusaku Sako <yu...@hortonworks.com>
Committed: Tue Sep 22 10:26:10 2015 -0700

----------------------------------------------------------------------
 .../main/admin/kerberos/wizard_controller.js    |   5 +-
 ambari-web/app/controllers/main/host/details.js | 124 +++++++----
 ambari-web/app/controllers/main/service/item.js |   7 +-
 ambari-web/app/mixins.js                        |   3 -
 .../host/details/actions/install_new_version.js |  76 -------
 .../host_components/install_component.js        | 126 -----------
 .../details/support_client_configs_download.js  |  86 --------
 .../wizard/wizardProgressPageController.js      |   5 +-
 ambari-web/app/utils/components.js              | 221 +++++++++++++++++++
 .../test/controllers/main/host/details_test.js  |  99 ++++++---
 .../test/controllers/main/service/item_test.js  |   9 +-
 .../wizard/wizardProgressPageController_test.js |   4 +-
 12 files changed, 389 insertions(+), 376 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/app/controllers/main/admin/kerberos/wizard_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/kerberos/wizard_controller.js b/ambari-web/app/controllers/main/admin/kerberos/wizard_controller.js
index 6fb0f8b..3476b2b 100644
--- a/ambari-web/app/controllers/main/admin/kerberos/wizard_controller.js
+++ b/ambari-web/app/controllers/main/admin/kerberos/wizard_controller.js
@@ -18,8 +18,9 @@
 
 
 var App = require('app');
+var componentsUtils = require('utils/components');
 
-App.KerberosWizardController = App.WizardController.extend(App.InstallComponent, {
+App.KerberosWizardController = App.WizardController.extend({
 
   exceptionsOnSkipClient: [{'KDC': 'realm'}, {'KDC': 'kdc_type'}, {'Advanced kerberos-env': 'executable_search_paths'}],
 
@@ -183,7 +184,7 @@ App.KerberosWizardController = App.WizardController.extend(App.InstallComponent,
   createKerberosResources: function (callback) {
     var self = this;
     this.createKerberosService().done(function () {
-      self.updateAndCreateServiceComponent('KERBEROS_CLIENT').done(function () {
+      componentsUtils.updateAndCreateServiceComponent('KERBEROS_CLIENT').done(function () {
         self.createKerberosHostComponents().done(callback);
       });
     });

http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/app/controllers/main/host/details.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/host/details.js b/ambari-web/app/controllers/main/host/details.js
index 40c645b..e4b27c6 100644
--- a/ambari-web/app/controllers/main/host/details.js
+++ b/ambari-web/app/controllers/main/host/details.js
@@ -18,10 +18,11 @@
 
 var App = require('app');
 var batchUtils = require('utils/batch_scheduled_requests');
+var componentsUtils = require('utils/components');
 var hostsManagement = require('utils/hosts');
 var stringUtils = require('utils/string_utils');
 
-App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDownload, App.InstallComponent, App.InstallNewVersion, {
+App.MainHostDetailsController = Em.Controller.extend({
 
   name: 'mainHostDetailsController',
 
@@ -177,6 +178,19 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   },
 
   /**
+   * Default error-callback for ajax-requests in current page
+   * @param {object} request
+   * @param {object} ajaxOptions
+   * @param {string} error
+   * @param {object} opt
+   * @param {object} params
+   * @method ajaxErrorCallback
+   */
+  ajaxErrorCallback: function (request, ajaxOptions, error, opt, params) {
+    return componentsUtils.ajaxErrorCallback(request, ajaxOptions, error, opt, params);
+  },
+
+  /**
    * Return true if hdfs user data is loaded via App.MainServiceInfoConfigsController
    */
   getHdfsUser: function () {
@@ -567,7 +581,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
       component = event.context,
       hostName = event.selectedHost || this.get('content.hostName'),
       componentName = component.get('componentName'),
-      missedComponents = event.selectedHost ? [] : this.checkComponentDependencies(componentName, {
+      missedComponents = event.selectedHost ? [] : componentsUtils.checkComponentDependencies(componentName, {
         scope: 'host',
         installedComponents: this.get('content.hostComponents').mapProperty('componentName')
       }),
@@ -585,7 +599,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
     switch (componentName) {
       case 'ZOOKEEPER_SERVER':
         returnFunc = App.showConfirmationPopup(function () {
-          self.installHostComponentCall(self.get('content.hostName'), component)
+          self.primary(component);
         }, Em.I18n.t('hosts.host.addComponent.' + componentName) + manualKerberosWarning);
         break;
       case 'HIVE_METASTORE':
@@ -620,7 +634,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
     var message = this.formatClientsMessage(component);
 
     return this.showAddComponentPopup(message, isManualKerberos, function () {
-      self.installHostComponentCall(self.get('content.hostName'), component);
+      self.primary(component);
     });
   },
 
@@ -668,6 +682,16 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   },
 
   /**
+   * Send request to add host component
+   * @param {App.HostComponent} component
+   * @method primary
+   */
+  primary: function (component) {
+    var self = this;
+    componentsUtils.installHostComponent(self.get('content.hostName'), component);
+  },
+
+  /**
    * Success callback for install host component request (sent in <code>addNewComponentSuccessCallback</code>)
    * @param {object} data
    * @param {object} opt
@@ -947,7 +971,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
       App.router.get('mainServiceInfoConfigsController').loadStep();
     }
     if (params.host) {
-      this.installHostComponentCall(params.host, App.StackServiceComponent.find(params.componentName));
+      componentsUtils.installHostComponent(params.host, App.StackServiceComponent.find(params.componentName));
     }
   },
   /**
@@ -2271,7 +2295,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   },
 
   downloadClientConfigs: function (event) {
-    this.downloadClientConfigsCall({
+    componentsUtils.downloadClientConfigs.call(this, {
       hostName: event.context.get('hostName'),
       componentName: event.context.get('componentName'),
       displayName: event.context.get('displayName')
@@ -2292,7 +2316,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
       }
     });
     clientsToAdd.forEach(function (component, index, array) {
-      var dependencies = this.checkComponentDependencies(component.get('componentName'), {
+      var dependencies = componentsUtils.checkComponentDependencies(component.get('componentName'), {
         scope: 'host',
         installedComponents: this.get('content.hostComponents').mapProperty('componentName')
       }).reject(function (componentName) {
@@ -2324,8 +2348,8 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
             self.showAddComponentPopup(message, isManualKerberos, function () {
               sendInstallCommand();
               clientsToAdd.forEach(function (component) {
-                self.installHostComponentCall(self.get('content.hostName'), component);
-              });
+                this.primary(component);
+              }, self);
             });
           } else {
             sendInstallCommand();
@@ -2336,42 +2360,6 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   },
 
   /**
-   * Check if all required components are installed on host.
-   * Available options:
-   *  scope: 'host' - dependency level `host`,`cluster` or `*`.
-   *  hostName: 'example.com' - host name to search installed components
-   *  installedComponents: ['A', 'B'] - names of installed components
-   *
-   * By default scope level is `*`
-   * For host level dependency you should specify at least `hostName` or `installedComponents` attribute.
-   *
-   * @param {String} componentName
-   * @param {Object} opt - options. Allowed options are `hostName`, `installedComponents`, `scope`.
-   * @return {Array} - names of missed components
-   */
-  checkComponentDependencies: function (componentName, opt) {
-    opt = opt || {};
-    opt.scope = opt.scope || '*';
-    var installedComponents;
-    var dependencies = App.StackServiceComponent.find(componentName).get('dependencies');
-    dependencies = opt.scope === '*' ? dependencies : dependencies.filterProperty('scope', opt.scope);
-    if (dependencies.length == 0) return [];
-    switch (opt.scope) {
-      case 'host':
-        Em.assert("You should pass at least `hostName` or `installedComponents` to options.", opt.hostName || opt.installedComponents);
-        installedComponents = opt.installedComponents || App.HostComponent.find().filterProperty('hostName', opt.hostName).mapProperty('componentName').uniq();
-        break;
-      default:
-        // @todo: use more appropriate value regarding installed components
-        installedComponents = opt.installedComponents || App.HostComponent.find().mapProperty('componentName').uniq();
-        break;
-    }
-    return dependencies.filter(function (dependency) {
-      return !installedComponents.contains(dependency.componentName);
-    }).mapProperty('componentName');
-  },
-
-  /**
    * On click handler for custom command from items menu
    * @param context
    */
@@ -2415,6 +2403,52 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   },
 
   /**
+   * show popup confirmation of version installation
+   * @param event
+   */
+  installVersionConfirmation: function (event) {
+    var self = this;
+
+    return App.showConfirmationPopup(function () {
+        self.installVersion(event);
+      },
+      Em.I18n.t('hosts.host.stackVersions.install.confirmation').format(event.context.get('displayName'))
+    );
+  },
+
+  /**
+   * install HostStackVersion on host
+   * @param {object} event
+   */
+  installVersion: function (event) {
+    App.ajax.send({
+      name: 'host.stack_versions.install',
+      sender: this,
+      data: {
+        hostName: this.get('content.hostName'),
+        version: event.context
+      },
+      success: 'installVersionSuccessCallback'
+    });
+  },
+
+  /**
+   * success callback of <code>installVersion</code>
+   * on success set version status to INSTALLING
+   * @param {object} data
+   * @param {object} opt
+   * @param {object} params
+   */
+  installVersionSuccessCallback: function (data, opt, params) {
+    App.HostStackVersion.find(params.version.get('id')).set('status', 'INSTALLING');
+    App.db.set('repoVersionInstall', 'id', [data.Requests.id]);
+    App.clusterStatus.setClusterStatus({
+      wizardControllerName: this.get('name'),
+      localdb: App.db.data
+    });
+  },
+
+  /**
    * Call callback after loading service metrics
    * @param callback
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/app/controllers/main/service/item.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/item.js b/ambari-web/app/controllers/main/service/item.js
index 6e1151e..99c2061 100644
--- a/ambari-web/app/controllers/main/service/item.js
+++ b/ambari-web/app/controllers/main/service/item.js
@@ -18,8 +18,9 @@
 
 var App = require('app');
 var batchUtils = require('utils/batch_scheduled_requests');
+var componentsUtils = require('utils/components');
 
-App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDownload, {
+App.MainServiceItemController = Em.Controller.extend({
   name: 'mainServiceItemController',
 
   /**
@@ -807,7 +808,7 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow
             }
           );
         } else {
-          self.installHostComponentCall(selectedHost, component);
+          componentsUtils.installHostComponent(selectedHost, component);
         }
 
         // Remove host from 'without' collection to immediate recalculate add menu item state
@@ -887,7 +888,7 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow
 
   downloadClientConfigs: function (event) {
     var component = this.get('content.clientComponents').rejectProperty('totalCount', 0)[0];
-    this.downloadClientConfigsCall({
+    componentsUtils.downloadClientConfigs.call(this, {
       serviceName: this.get('content.serviceName'),
       componentName: (event && event.name) || component.get('componentName'),
       displayName: (event && event.label) || component.get('displayName')

http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/app/mixins.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins.js b/ambari-web/app/mixins.js
index 6552c61..d86eccf 100644
--- a/ambari-web/app/mixins.js
+++ b/ambari-web/app/mixins.js
@@ -27,9 +27,6 @@ require('mixins/common/serverValidator');
 require('mixins/common/table_server_view_mixin');
 require('mixins/common/table_server_mixin');
 require('mixins/main/host/details/host_components/decommissionable');
-require('mixins/main/host/details/host_components/install_component');
-require('mixins/main/host/details/actions/install_new_version');
-require('mixins/main/host/details/support_client_configs_download');
 require('mixins/main/service/groups_mapping');
 require('mixins/main/service/themes_mapping');
 require('mixins/main/service/versions_mapping');

http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/app/mixins/main/host/details/actions/install_new_version.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/main/host/details/actions/install_new_version.js b/ambari-web/app/mixins/main/host/details/actions/install_new_version.js
deleted file mode 100644
index 44e2550..0000000
--- a/ambari-web/app/mixins/main/host/details/actions/install_new_version.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * 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');
-
-App.InstallNewVersion = Em.Mixin.create({
-
-  /**
-   * show popup confirmation of version installation
-   * @param event
-   */
-  installVersionConfirmation: function (event) {
-    var self = this;
-
-    return App.showConfirmationPopup(function () {
-        self.installVersion(event);
-      },
-      Em.I18n.t('hosts.host.stackVersions.install.confirmation').format(event.context.get('displayName'))
-    );
-  },
-
-  /**
-   * install HostStackVersion on host
-   * @param {object} event
-   */
-  installVersion: function (event) {
-    App.ajax.send({
-      name: 'host.stack_versions.install',
-      sender: this,
-      data: {
-        hostName: this.get('content.hostName'),
-        version: event.context
-      },
-      success: 'installVersionSuccessCallback'
-    });
-  },
-
-  /**
-   * success callback of <code>installVersion</code>
-   * on success set version status to INSTALLING
-   * @param {object} data
-   * @param {object} opt
-   * @param {object} params
-   */
-  installVersionSuccessCallback: function (data, opt, params) {
-    App.HostStackVersion.find(params.version.get('id')).set('status', 'INSTALLING');
-    App.db.set('repoVersionInstall', 'id', [data.Requests.id]);
-    App.clusterStatus.setClusterStatus({
-      wizardControllerName: this.get('name'),
-      localdb: App.db.data
-    });
-  },
-
-  /**
-   * Call callback after loading service metrics
-   * @param callback
-   */
-  isServiceMetricsLoaded: function(callback) {
-    App.router.get('mainController').isLoading.call(App.router.get('clusterController'), 'isServiceContentFullyLoaded').done(callback);
-  }
-
-});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/app/mixins/main/host/details/host_components/install_component.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/main/host/details/host_components/install_component.js b/ambari-web/app/mixins/main/host/details/host_components/install_component.js
deleted file mode 100644
index 858c95f..0000000
--- a/ambari-web/app/mixins/main/host/details/host_components/install_component.js
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-var App = require('app');
-
-App.InstallComponent = Em.Mixin.create({
-
-  installHostComponentCall: function (hostName, component) {
-    var self = this,
-      componentName = component.get('componentName'),
-      displayName = component.get('displayName');
-    this.updateAndCreateServiceComponent(componentName).done(function () {
-      return App.ajax.send({
-        name: 'host.host_component.add_new_component',
-        sender: self,
-        data: {
-          hostName: hostName,
-          component: component,
-          data: JSON.stringify({
-            RequestInfo: {
-              "context": Em.I18n.t('requestInfo.installHostComponent') + " " + displayName
-            },
-            Body: {
-              host_components: [
-                {
-                  HostRoles: {
-                    component_name: componentName
-                  }
-                }
-              ]
-            }
-          })
-        },
-        success: 'addNewComponentSuccessCallback',
-        error: 'ajaxErrorCallback'
-      });
-    });
-  },
-
-  /**
-   * Success callback for add host component request
-   * @param {object} data
-   * @param {object} opt
-   * @param {object} params
-   * @return {$.ajax}
-   * @method addNewComponentSuccessCallback
-   */
-  addNewComponentSuccessCallback: function (data, opt, params) {
-    return App.ajax.send({
-      name: 'common.host.host_component.update',
-      sender: App.router.get('mainHostDetailsController'),
-      data: {
-        hostName: params.hostName,
-        componentName: params.component.get('componentName'),
-        serviceName: params.component.get('serviceName'),
-        component: params.component,
-        "context": Em.I18n.t('requestInfo.installNewHostComponent') + " " + params.component.get('displayName'),
-        HostRoles: {
-          state: 'INSTALLED'
-        },
-        urlParams: "HostRoles/state=INIT"
-      },
-      success: 'installNewComponentSuccessCallback',
-      error: 'ajaxErrorCallback'
-    });
-  },
-
-  /**
-   * Default error-callback for ajax-requests in current page
-   * @param {object} request
-   * @param {object} ajaxOptions
-   * @param {string} error
-   * @param {object} opt
-   * @param {object} params
-   * @method ajaxErrorCallback
-   */
-  ajaxErrorCallback: function (request, ajaxOptions, error, opt, params) {
-    App.ajax.defaultErrorHandler(request, opt.url, opt.method);
-  },
-
-  /**
-   *
-   * @param componentName
-   * @returns {*}
-   */
-  updateAndCreateServiceComponent: function (componentName) {
-    var self = this;
-    var dfd = $.Deferred();
-    var updater =  App.router.get('updateController');
-    updater.updateComponentsState(function () {
-      updater.updateServiceMetric(function () {
-        self.createServiceComponent(componentName, dfd);
-      });
-    });
-    return dfd.promise();
-  },
-
-  /**
-   *
-   * @param componentName
-   * @param dfd
-   * @returns {*}
-   */
-  createServiceComponent: function (componentName, dfd) {
-    var allServiceComponents = [];
-    var services = App.Service.find().mapProperty('serviceName');
-    services.forEach(function (_service) {
-      var _serviceComponents = App.Service.find(_service).get('serviceComponents');
-      allServiceComponents = allServiceComponents.concat(_serviceComponents);
-    }, this);
-    if (allServiceComponents.contains(componentName)) {
-      dfd.resolve();
-    } else {
-      return App.ajax.send({
-        name: 'common.create_component',
-        sender: this,
-        data: {
-          componentName: componentName,
-          serviceName: App.StackServiceComponent.find().findProperty('componentName', componentName).get('serviceName')
-        }
-      }).complete(function () {
-        dfd.resolve();
-      });
-    }
-  }
-
-});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/app/mixins/main/host/details/support_client_configs_download.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/main/host/details/support_client_configs_download.js b/ambari-web/app/mixins/main/host/details/support_client_configs_download.js
deleted file mode 100644
index 5d02190..0000000
--- a/ambari-web/app/mixins/main/host/details/support_client_configs_download.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * 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');
-
-App.SupportClientConfigsDownload = Em.Mixin.create({
-
-  /**
-   *
-   * @param {{hostName: string, componentName: string, displayName: string, serviceName: string}} data
-   */
-  downloadClientConfigsCall: function (data) {
-    var url = this._getUrl(data.hostName, data.serviceName, data.componentName);
-    try {
-      var self = this;
-      $.fileDownload(url).fail(function (error) {
-        var errorMessage = '';
-        var isNoConfigs = false;
-        if (error && $(error).text()) {
-          var errorObj = JSON.parse($(error).text());
-          if (errorObj && errorObj.message && errorObj.status) {
-            isNoConfigs = errorObj.message.indexOf(Em.I18n.t('services.service.actions.downloadClientConfigs.fail.noConfigFile')) !== -1;
-            errorMessage += isNoConfigs ? Em.I18n.t('services.service.actions.downloadClientConfigs.fail.noConfigFile') :
-              Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.body.errorMessage').format(data.displayName, errorObj.status, errorObj.message);
-          }
-          else {
-            errorMessage += Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.body.noErrorMessage').format(data.displayName);
-          }
-          errorMessage += isNoConfigs ? '' : Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.body.question');
-        }
-        else {
-          errorMessage += Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.body.noErrorMessage').format(data.displayName) +
-            Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.body.question');
-        }
-        return App.ModalPopup.show({
-          header: Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.header').format(data.displayName),
-          bodyClass: Em.View.extend({
-            template: Em.Handlebars.compile(errorMessage)
-          }),
-          secondary: isNoConfigs ? false : Em.I18n.t('common.cancel'),
-          onPrimary: function () {
-            this.hide();
-            if (!isNoConfigs) {
-              self.downloadClientConfigs({
-                context: Em.Object.create(data)
-              })
-            }
-          }
-        });
-      });
-    } catch (err) {
-      var newWindow = window.open(url);
-      newWindow.focus();
-    }
-  },
-
-  /**
-   *
-   * @param {string|null} hostName
-   * @param {string} serviceName
-   * @param {string} componentName
-   * @returns {string}
-   * @private
-   */
-  _getUrl: function (hostName, serviceName, componentName) {
-    var isForHost = !Em.isNone(hostName);
-    return App.get('apiPrefix') + '/clusters/' + App.router.getClusterName() + '/' +
-      (isForHost ? 'hosts/' + hostName + '/host_components/' : 'services/' + serviceName + '/components/') +
-      componentName + '?format=client_config_tar';
-  }
-
-});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/app/mixins/wizard/wizardProgressPageController.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/wizard/wizardProgressPageController.js b/ambari-web/app/mixins/wizard/wizardProgressPageController.js
index e0440c4..d290173 100644
--- a/ambari-web/app/mixins/wizard/wizardProgressPageController.js
+++ b/ambari-web/app/mixins/wizard/wizardProgressPageController.js
@@ -17,13 +17,14 @@
  */
 
 var App = require('app');
+var componentsUtils = require('utils/components');
 
 /**
  * Mixin for wizard controller for showing command progress on wizard pages
  * This should
  * @type {Ember.Mixin}
  */
-App.wizardProgressPageControllerMixin = Em.Mixin.create(App.InstallComponent, {
+App.wizardProgressPageControllerMixin = Em.Mixin.create({
   controllerName: '',
   clusterDeployState: 'WIZARD_DEPLOY',
   status: 'IN_PROGRESS',
@@ -516,7 +517,7 @@ App.wizardProgressPageControllerMixin = Em.Mixin.create(App.InstallComponent, {
         }
       };
       if (!!hostsWithoutComponents.length) {
-        self.updateAndCreateServiceComponent(componentName).done(function () {
+        componentsUtils.updateAndCreateServiceComponent(componentName).done(function () {
           App.ajax.send({
             name: 'wizard.step8.register_host_to_component',
             sender: self,

http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/app/utils/components.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/components.js b/ambari-web/app/utils/components.js
new file mode 100644
index 0000000..54ee6c0
--- /dev/null
+++ b/ambari-web/app/utils/components.js
@@ -0,0 +1,221 @@
+/**
+ * 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');
+
+module.exports = {
+  installHostComponent: function (hostName, component) {
+    var self = this,
+      componentName = component.get('componentName'),
+      displayName = component.get('displayName');
+    this.updateAndCreateServiceComponent(componentName).done(function () {
+      App.ajax.send({
+        name: 'host.host_component.add_new_component',
+        sender: self,
+        data: {
+          hostName: hostName,
+          component: component,
+          data: JSON.stringify({
+            RequestInfo: {
+              "context": Em.I18n.t('requestInfo.installHostComponent') + " " + displayName
+            },
+            Body: {
+              host_components: [
+                {
+                  HostRoles: {
+                    component_name: componentName
+                  }
+                }
+              ]
+            }
+          })
+        },
+        success: 'addNewComponentSuccessCallback',
+        error: 'ajaxErrorCallback'
+      });
+    });
+  },
+
+  /**
+   * Success callback for add host component request
+   * @param {object} data
+   * @param {object} opt
+   * @param {object} params
+   * @method addNewComponentSuccessCallback
+   */
+  addNewComponentSuccessCallback: function (data, opt, params) {
+    console.log('Send request for ADDING NEW COMPONENT successfully');
+    App.ajax.send({
+      name: 'common.host.host_component.update',
+      sender: App.router.get('mainHostDetailsController'),
+      data: {
+        hostName: params.hostName,
+        componentName: params.component.get('componentName'),
+        serviceName: params.component.get('serviceName'),
+        component: params.component,
+        "context": Em.I18n.t('requestInfo.installNewHostComponent') + " " + params.component.get('displayName'),
+        HostRoles: {
+          state: 'INSTALLED'
+        },
+        urlParams: "HostRoles/state=INIT"
+      },
+      success: 'installNewComponentSuccessCallback',
+      error: 'ajaxErrorCallback'
+    });
+  },
+
+  /**
+   * Default error-callback for ajax-requests in current page
+   * @param {object} request
+   * @param {object} ajaxOptions
+   * @param {string} error
+   * @param {object} opt
+   * @param {object} params
+   * @method ajaxErrorCallback
+   */
+  ajaxErrorCallback: function (request, ajaxOptions, error, opt, params) {
+    console.log('error on change component host status');
+    App.ajax.defaultErrorHandler(request, opt.url, opt.method);
+  },
+
+  downloadClientConfigs: function (data) {
+    var isForHost = typeof data.hostName !== 'undefined';
+    var url = App.get('apiPrefix') + '/clusters/' + App.router.getClusterName() + '/' +
+      (isForHost ? 'hosts/' + data.hostName + '/host_components/' : 'services/' + data.serviceName + '/components/') +
+      data.componentName + '?format=client_config_tar';
+    try {
+      var self = this;
+      $.fileDownload(url).fail(function (error) {
+        var errorMessage = '';
+        var isNoConfigs = false;
+        if (error && $(error).text()) {
+          var errorObj = JSON.parse($(error).text());
+          if (errorObj && errorObj.message && errorObj.status) {
+            isNoConfigs = errorObj.message.indexOf(Em.I18n.t('services.service.actions.downloadClientConfigs.fail.noConfigFile')) !== -1;
+            errorMessage += isNoConfigs ? Em.I18n.t('services.service.actions.downloadClientConfigs.fail.noConfigFile') :
+              Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.body.errorMessage').format(data.displayName, errorObj.status, errorObj.message);
+          } else {
+            errorMessage += Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.body.noErrorMessage').format(data.displayName);
+          }
+          errorMessage += isNoConfigs ? '' : Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.body.question');
+        } else {
+          errorMessage += Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.body.noErrorMessage').format(data.displayName) +
+            Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.body.question');
+        }
+        App.ModalPopup.show({
+          header: Em.I18n.t('services.service.actions.downloadClientConfigs.fail.popup.header').format(data.displayName),
+          bodyClass: Ember.View.extend({
+            template: Em.Handlebars.compile(errorMessage)
+          }),
+          secondary: isNoConfigs ? false : Em.I18n.t('common.cancel'),
+          onPrimary: function () {
+            this.hide();
+            if (!isNoConfigs) {
+              self.downloadClientConfigs({
+                context: Em.Object.create(data)
+              })
+            }
+          }
+        });
+      });
+    } catch (err) {
+      var newWindow = window.open(url);
+      newWindow.focus();
+    }
+  },
+  /**
+   * Check if all required components are installed on host.
+   * Available options:
+   *  scope: 'host' - dependency level `host`,`cluster` or `*`.
+   *  hostName: 'example.com' - host name to search installed components
+   *  installedComponents: ['A', 'B'] - names of installed components
+   *
+   * By default scope level is `*`
+   * For host level dependency you should specify at least `hostName` or `installedComponents` attribute.
+   *
+   * @param {String} componentName
+   * @param {Object} opt - options. Allowed options are `hostName`, `installedComponents`, `scope`.
+   * @return {Array} - names of missed components
+   */
+  checkComponentDependencies: function (componentName, opt) {
+    opt = opt || {};
+    opt.scope = opt.scope || '*';
+    var installedComponents;
+    var dependencies = App.StackServiceComponent.find(componentName).get('dependencies');
+    dependencies = opt.scope === '*' ? dependencies : dependencies.filterProperty('scope', opt.scope);
+    if (dependencies.length == 0) return [];
+    switch (opt.scope) {
+      case 'host':
+        Em.assert("You should pass at least `hostName` or `installedComponents` to options.", opt.hostName || opt.installedComponents);
+        installedComponents = opt.installedComponents || App.HostComponent.find().filterProperty('hostName', opt.hostName).mapProperty('componentName').uniq();
+        break;
+      default:
+        // @todo: use more appropriate value regarding installed components
+        installedComponents = opt.installedComponents || App.HostComponent.find().mapProperty('componentName').uniq();
+        break;
+    }
+    return dependencies.filter(function (dependency) {
+      return !installedComponents.contains(dependency.componentName);
+    }).mapProperty('componentName');
+  },
+
+  /**
+   *
+   * @param componentName
+   * @returns {*}
+   */
+  updateAndCreateServiceComponent: function (componentName) {
+    var self = this;
+    var dfd = $.Deferred();
+    var updater =  App.router.get('updateController');
+    updater.updateComponentsState(function () {
+      updater.updateServiceMetric(function () {
+        self.createServiceComponent(componentName, dfd);
+      });
+    });
+    return dfd.promise();
+  },
+
+  /**
+   *
+   * @param componentName
+   * @param dfd
+   * @returns {*}
+   */
+  createServiceComponent: function (componentName, dfd) {
+    var allServiceComponents = [];
+    var services = App.Service.find().mapProperty('serviceName');
+    services.forEach(function (_service) {
+      var _serviceComponents = App.Service.find(_service).get('serviceComponents');
+      allServiceComponents = allServiceComponents.concat(_serviceComponents);
+    }, this);
+    if (allServiceComponents.contains(componentName)) {
+      dfd.resolve();
+    } else {
+      App.ajax.send({
+        name: 'common.create_component',
+        sender: this,
+        data: {
+          componentName: componentName,
+          serviceName: App.StackServiceComponent.find().findProperty('componentName', componentName).get('serviceName')
+        }
+      }).complete(function () {
+        dfd.resolve();
+      });
+    }
+  }
+};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/test/controllers/main/host/details_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/host/details_test.js b/ambari-web/test/controllers/main/host/details_test.js
index 6121cc5..463bbc2 100644
--- a/ambari-web/test/controllers/main/host/details_test.js
+++ b/ambari-web/test/controllers/main/host/details_test.js
@@ -23,6 +23,7 @@ require('models/service');
 require('models/host_component');
 require('models/host_stack_version');
 var batchUtils = require('utils/batch_scheduled_requests');
+var componentsUtils = require('utils/components');
 var hostsManagement = require('utils/hosts');
 var controller;
 
@@ -34,7 +35,7 @@ describe('App.MainHostDetailsController', function () {
       then: Em.K,
       complete: Em.K
     });
-    controller = App.MainHostDetailsController.create(App.InstallComponent, {
+    controller = App.MainHostDetailsController.create({
       content: Em.Object.create({
         hostComponents: []
       })
@@ -225,11 +226,11 @@ describe('App.MainHostDetailsController', function () {
   });
 
   describe('#ajaxErrorCallback()', function () {
-    it('call mainServiceItemController.ajaxErrorCallback', function () {
-      sinon.stub(controller, 'ajaxErrorCallback', Em.K);
+    it('call componentsUtils.ajaxErrorCallback', function () {
+      sinon.stub(componentsUtils, 'ajaxErrorCallback', Em.K);
       controller.ajaxErrorCallback('request', 'ajaxOptions', 'error', 'opt', 'params');
-      expect(controller.ajaxErrorCallback.calledWith('request', 'ajaxOptions', 'error', 'opt', 'params')).to.be.true;
-      controller.ajaxErrorCallback.restore();
+      expect(componentsUtils.ajaxErrorCallback.calledWith('request', 'ajaxOptions', 'error', 'opt', 'params')).to.be.true;
+      componentsUtils.ajaxErrorCallback.restore();
     });
   });
 
@@ -477,23 +478,18 @@ describe('App.MainHostDetailsController', function () {
     beforeEach(function () {
       sinon.spy(App, "showConfirmationPopup");
       sinon.stub(controller, "addClientComponent", Em.K);
-      sinon.stub(controller, "installHostComponentCall", Em.K);
-      sinon.stub(controller, "checkComponentDependencies", Em.K);
-      controller.set('content', {
-        hostComponents: [Em.Object.create({
-          componentName: "HDFS_CLIENT"
-        })]
-      });
-      controller.reopen({
-        securityEnabled: false
-      });
+      sinon.stub(controller, "primary", Em.K);
+      controller.set('content', {hostComponents: [Em.Object.create({
+        componentName: "HDFS_CLIENT"
+      })]});
+      sinon.stub(componentsUtils, 'checkComponentDependencies', Em.K);
     });
 
     afterEach(function () {
       App.showConfirmationPopup.restore();
       controller.addClientComponent.restore();
-      controller.installHostComponentCall.restore();
-      controller.checkComponentDependencies.restore();
+      controller.primary.restore();
+      componentsUtils.checkComponentDependencies.restore();
     });
 
     it('add ZOOKEEPER_SERVER', function () {
@@ -502,8 +498,12 @@ describe('App.MainHostDetailsController', function () {
           componentName: 'ZOOKEEPER_SERVER'
         })
       };
-      controller.addComponent(event);
+      var popup = controller.addComponent(event);
       expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.primary.calledWith(Em.Object.create({
+        componentName: 'ZOOKEEPER_SERVER'
+      }))).to.be.true;
     });
     it('add slave component', function () {
       var event = {
@@ -591,16 +591,60 @@ describe('App.MainHostDetailsController', function () {
 
     beforeEach(function () {
       sinon.spy(App.ModalPopup, 'show');
+      sinon.stub(controller, 'primary', Em.K);
     });
 
     afterEach(function () {
       App.ModalPopup.show.restore();
+      controller.primary.restore();
     });
 
     it('should display add component confirmation', function () {
-      var popup = controller.showAddComponentPopup(message, false, Em.K);
+      var popup = controller.showAddComponentPopup(message, false, function () {
+        controller.primary(component);
+      });
       expect(App.ModalPopup.show.calledOnce).to.be.true;
       expect(popup.get('addComponentMsg')).to.eql(Em.I18n.t('hosts.host.addComponent.msg').format(message));
+      popup.onPrimary();
+      expect(controller.primary.calledWith(component)).to.be.true;
+    });
+  });
+
+  describe('#primary()', function () {
+    beforeEach(function () {
+      sinon.stub(App.StackServiceComponent, 'find', function () {
+        return [
+          Em.Object.create({
+            componentName: 'COMP1',
+            serviceName: 's1'
+          })
+        ]
+      });
+
+      sinon.stub(App.router, 'get', function () {
+        return Em.Object.create({
+          updateComponentsState: function (callback) {
+            return callback();
+          },
+          updateServiceMetric: function (callback) {
+            return callback();
+          }
+        })
+      });
+    });
+    afterEach(function () {
+      App.router.get.restore();
+      App.StackServiceComponent.find.restore();
+    });
+
+    it('Query should be sent', function () {
+      var component = Em.Object.create({
+        componentName: 'COMP1',
+        displayName: 'comp1'
+      });
+      App.serviceComponents = ['COMP1'];
+      controller.primary(component);
+      expect(App.ajax.send.calledOnce).to.be.true;
     });
   });
 
@@ -2049,16 +2093,16 @@ describe('App.MainHostDetailsController', function () {
       }))).to.equal(0);
     });
   });
-  describe('#downloadClientConfigsCall', function () {
+  describe('#downloadClientConfigs()', function () {
 
     beforeEach(function () {
-      sinon.stub(controller, 'downloadClientConfigsCall', Em.K);
+      sinon.stub(componentsUtils, 'downloadClientConfigs', Em.K);
     });
     afterEach(function () {
-      controller.downloadClientConfigsCall.restore();
+      componentsUtils.downloadClientConfigs.restore();
     });
 
-    it('should launch controller.downloadClientConfigsCall method', function () {
+    it('should launch componentsUtils.downloadClientConfigs method', function () {
       controller.downloadClientConfigs({
         context: Em.Object.create({
           componentName: 'name',
@@ -2066,7 +2110,7 @@ describe('App.MainHostDetailsController', function () {
           displayName: 'dName'
         })
       });
-      expect(controller.downloadClientConfigsCall.calledWith({
+      expect(componentsUtils.downloadClientConfigs.calledWith({
         componentName: 'name',
         hostName: 'host1',
         displayName: 'dName'
@@ -2434,7 +2478,8 @@ describe('App.MainHostDetailsController', function () {
           showAlertPopupCalled: false,
           title: 'Clients to add have mutual dependencies'
         }
-      ];
+      ],
+      componentsUtils = require('utils/components');
 
     beforeEach(function () {
       sinon.stub(controller, 'sendComponentCommand', Em.K);
@@ -2460,12 +2505,12 @@ describe('App.MainHostDetailsController', function () {
       App.get('router.mainAdminKerberosController').getSecurityType.restore();
       App.showAlertPopup.restore();
       App.StackServiceComponent.find.restore();
-      controller.checkComponentDependencies.restore();
+      componentsUtils.checkComponentDependencies.restore();
     });
 
     cases.forEach(function (item) {
       it(item.title, function () {
-        sinon.stub(controller, 'checkComponentDependencies', function (componentName, params) {
+        sinon.stub(componentsUtils, 'checkComponentDependencies', function (componentName, params) {
           return item.dependencies[componentName];
         });
         controller.installClients({

http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/test/controllers/main/service/item_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/service/item_test.js b/ambari-web/test/controllers/main/service/item_test.js
index b3151dd..f27508b 100644
--- a/ambari-web/test/controllers/main/service/item_test.js
+++ b/ambari-web/test/controllers/main/service/item_test.js
@@ -27,6 +27,7 @@ require('controllers/global/cluster_controller');
 require('controllers/main/service/reassign_controller');
 require('controllers/main/service/item');
 var batchUtils = require('utils/batch_scheduled_requests');
+var componentsUtils = require('utils/components');
 
 describe('App.MainServiceItemController', function () {
 
@@ -1075,15 +1076,15 @@ describe('App.MainServiceItemController', function () {
     });
 
     beforeEach(function () {
-      sinon.stub(mainServiceItemController, 'downloadClientConfigsCall', Em.K);
+      sinon.stub(componentsUtils, 'downloadClientConfigs', Em.K);
     });
     afterEach(function () {
-      mainServiceItemController.downloadClientConfigsCall.restore();
+      componentsUtils.downloadClientConfigs.restore();
     });
 
     it('should launch $.fileDownload method', function () {
       mainServiceItemController.downloadClientConfigs();
-      expect(mainServiceItemController.downloadClientConfigsCall.calledWith({
+      expect(componentsUtils.downloadClientConfigs.calledWith({
         serviceName: 'S1',
         componentName: 'C1',
         displayName: 'd1'
@@ -1095,7 +1096,7 @@ describe('App.MainServiceItemController', function () {
         name: 'name1'
       };
       mainServiceItemController.downloadClientConfigs(event);
-      expect(mainServiceItemController.downloadClientConfigsCall.calledWith({
+      expect(componentsUtils.downloadClientConfigs.calledWith({
         serviceName: 'S1',
         componentName: 'name1',
         displayName: 'label1'

http://git-wip-us.apache.org/repos/asf/ambari/blob/2ecda9da/ambari-web/test/mixins/wizard/wizardProgressPageController_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/mixins/wizard/wizardProgressPageController_test.js b/ambari-web/test/mixins/wizard/wizardProgressPageController_test.js
index 84efaf5..a9b8822 100644
--- a/ambari-web/test/mixins/wizard/wizardProgressPageController_test.js
+++ b/ambari-web/test/mixins/wizard/wizardProgressPageController_test.js
@@ -30,7 +30,7 @@ describe('App.wizardProgressPageControllerMixin', function() {
           callback();
         }});
       });
-      sinon.stub(mixedObjectInstance, "updateAndCreateServiceComponent").returns({
+      sinon.stub(require('utils/components'), "updateAndCreateServiceComponent").returns({
         done: function(callback) {
           return callback();
         }
@@ -70,7 +70,7 @@ describe('App.wizardProgressPageControllerMixin', function() {
     afterEach(function() {
       App.ajax.send.restore();
       App.StackServiceComponent.find.restore();
-      mixedObjectInstance.updateAndCreateServiceComponent.restore();
+      require('utils/components').updateAndCreateServiceComponent.restore();
       mixedObjectInstance.onCreateComponent.restore();
       mixedObjectInstance.updateComponent.restore();
       mixedObjectInstance.checkInstalledComponents.restore();