/**
 * 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');
var batchUtils = require('utils/batch_scheduled_requests');
var hostsManagement = require('utils/hosts');
var stringUtils = require('utils/string_utils');
require('utils/configs/add_component_config_initializer');

App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDownload, App.InstallComponent, App.InstallNewVersion, App.CheckHostMixin, App.TrackRequestMixin, {

  name: 'mainHostDetailsController',

  /**
   * Viewed host
   * @type {App.Host|null}
   */
  content: null,

  /**
   * Is check host procedure finished
   * @type {bool}
   */
  checkHostFinished: null,

  /**
   * Does user come from hosts page
   * @type {bool}
   */
  isFromHosts: false,

  /**
   * Determines whether we are adding Hive Server2 component
   * @type {bool}
   */
  addHiveServer: false,

  /**
   * Determines whether we are adding ZooKeeper Server component
   * @type {bool}
   */
  addZooKeeperServer: false,

  /**
   * path to page visited before
   * @type {string}
   */
  referer: '',

  /**
   *  Host on which Hive Metastore will be added
   * @type {string}
   */
  hiveMetastoreHost: '',

  /**
   * Deferred object will be resolved when Oozie configs are downloaded
   * @type {object}
   */
  isOozieConfigLoaded: $.Deferred(),

  /**
   * @type {bool}
   */
  isOozieServerAddable: true,

  isConfigsLoadingInProgress: false,

  addDeleteComponentsMap: {
    'ZOOKEEPER_SERVER': {
      addPropertyName: 'addZooKeeperServer',
      deletePropertyName: 'fromDeleteZkServer',
      configTagsCallbackName: 'loadZookeeperConfigs',
      configsCallbackName: 'saveZkConfigs'
    },
    'HIVE_METASTORE': {
      deletePropertyName: 'deleteHiveMetaStore',
      hostPropertyName: 'hiveMetastoreHost',
      configTagsCallbackName: 'loadHiveConfigs',
      configsCallbackName: 'onLoadHiveConfigs'
    },
    'WEBHCAT_SERVER': {
      deletePropertyName: 'deleteWebHCatServer',
      hostPropertyName: 'webhcatServerHost',
      configTagsCallbackName: 'loadWebHCatConfigs',
      configsCallbackName: 'onLoadHiveConfigs'
    },
    'HIVE_SERVER': {
      addPropertyName: 'addHiveServer',
      deletePropertyName: 'deleteHiveServer',
      configTagsCallbackName: 'loadHiveConfigs',
      configsCallbackName: 'onLoadHiveConfigs'
    },
    'NIMBUS': {
      deletePropertyName: 'deleteNimbusHost',
      hostPropertyName: 'nimbusHost',
      configTagsCallbackName: 'loadStormConfigs',
      configsCallbackName: 'onLoadStormConfigs'
    },
    'ATLAS_SERVER': {
      deletePropertyName: 'deleteAtlasServer',
      hostPropertyName: 'atlasServer',
      configTagsCallbackName: 'loadAtlasConfigs',
      configsCallbackName: 'onLoadAtlasConfigs'
    },
    'RANGER_KMS_SERVER': {
      deletePropertyName: 'deleteRangerKMSServer',
      hostPropertyName: 'rangerKMSServerHost',
      configTagsCallbackName: 'loadRangerConfigs',
      configsCallbackName: 'onLoadRangerConfigs'
    }
  },

  zooKeeperRelatedServices: [
    {
      serviceName: 'HIVE',
      typesToLoad: ['hive-site', 'webhcat-site'],
      typesToSave: ['hive-site', 'webhcat-site']
    },
    {
      serviceName: 'YARN',
      typesToLoad: ['yarn-site', 'zoo.cfg'],
      typesToSave: ['yarn-site']
    },
    {
      serviceName: 'HBASE',
      typesToLoad: ['hbase-site'],
      typesToSave: ['hbase-site']
    },
    {
      serviceName: 'ACCUMULO',
      typesToLoad: ['accumulo-site'],
      typesToSave: ['accumulo-site']
    },
    {
      serviceName: 'KAFKA',
      typesToLoad: ['kafka-broker'],
      typesToSave: ['kafka-broker']
    },
    {
      serviceName: 'ATLAS',
      typesToLoad: ['application-properties', 'infra-solr-env'],
      typesToSave: ['application-properties']
    },
    {
      serviceName: 'STORM',
      typesToLoad: ['storm-site'],
      typesToSave: ['storm-site']
    }
  ],

  /**
   * Determines whether adding/deleting host component requires configs changes
   * @type {Boolean}
   */
  isReconfigureRequired: false,

  /**
   * Contains component-related config properties loaded from server
   * @type {Object|null}
   */
  configs: null,

  /**
   * Array of all properties affected by adding/deleting host component
   * @type {Array}
   */
  allPropertiesToChange: [],

  /**
   * Array of editable properties affected by adding/deleting host component
   * @type {Array}
   */
  recommendedPropertiesToChange: [],

  /**
   * Array of non-editable properties affected by adding/deleting host component
   * @type {Array}
   */
  requiredPropertiesToChange: [],

  /**
   * Properties affected by adding/deleting host component, grouped by service, formatted for PUT call
   * @type {Array}
   */
  groupedPropertiesToChange: [],

  hasPropertiesToChange: Em.computed.or('recommendedPropertiesToChange.length', 'requiredPropertiesToChange.length'),

  addDeleteComponentPopupBody: Em.View.extend({
    templateName: require('templates/main/host/details/addDeleteComponentPopup'),
    commonMessage: '',
    manualKerberosWarning: App.get('router.mainAdminKerberosController.isManualKerberos') ?
      Em.I18n.t('hosts.host.manualKerberosWarning') : '',
    lastComponent: false,
    lastComponentError: '',
    fromServiceSummary: false,
    selectedHost: null,
    anyHostsWithoutComponent: true
  }),

  saveLoadedConfigs: function (data) {
    this.set('configs', {
      items: data.items.map((item) => {
        return {
          type: item.type,
          properties_attributes: item.properties_attributes,
          properties: Em.copy(item.properties)
        }
      })
    });
  },

  clearConfigsChanges: function (shouldKeepLoadedConfigs) {
    var arrayNames = ['allPropertiesToChange', 'recommendedPropertiesToChange', 'requiredPropertiesToChange', 'groupedPropertiesToChange'];
    this.abortRequests();
    arrayNames.forEach(function (arrayName) {
      this.get(arrayName).clear();
    }, this);
    this.set('isReconfigureRequired', false);
    if (!shouldKeepLoadedConfigs) {
      this.set('configs', null);
    }
  },

  applyConfigsCustomization: function () {
    this.get('recommendedPropertiesToChange').forEach(function (property) {
      var value = property.saveRecommended ? property.recommendedValue : property.initialValue,
        filename = property.propertyFileName;
      if (this.get('groupedPropertiesToChange.length')) {
        var group = this.get('groupedPropertiesToChange').find(function (item) {
          return item.properties.hasOwnProperty(filename);
        });
        if (group) {
          group.properties[filename][property.propertyName] = value;
        }
      }
    }, this);
  },

  /**
   * Open dashboard page
   * @method routeHome
   */
  routeHome: function () {
    App.router.transitionTo('main.dashboard.index');
  },

  /**
   * List of active (not in passive state) host components
   * @type {Ember.Enumerable}
   */
  serviceActiveComponents: function () {
    return this.get('content.hostComponents').filterProperty('service.isInPassive', false);
  }.property('content.hostComponents'),

  /**
   * List of active host components which aren't clients
   * @type {Ember.Enumerable}
   */
  serviceNonClientActiveComponents: Em.computed.filterBy('serviceActiveComponents', 'isClient', false),

  /**
   * send command to server to start selected host component
   * @param {object} event
   * @method startComponent
   */
  startComponent: function (event) {
    var self = this;
    var component = event.context;
    return App.showConfirmationPopup(function () {
      var context = Em.I18n.t('requestInfo.startHostComponent') + " " + component.get('displayName');
      self.sendComponentCommand(component, context, App.HostComponentStatus.started);
    }, Em.I18n.t('question.sure.start').format(component.get('displayName')));
  },

  /**
   * send command to server to stop selected host component
   * @param {object} event
   * @method startComponent
   */
  stopComponent: function (event) {
    var self = this;
    var component = event.context;
    if (event.context.get('componentName') === 'NAMENODE' ) {
      this.checkNnLastCheckpointTime(function () {
        return App.showConfirmationPopup(function () {
          var context = Em.I18n.t('requestInfo.stopHostComponent') + " " + component.get('displayName');
          self.sendComponentCommand(component, context, App.HostComponentStatus.stopped);
        }, Em.I18n.t('question.sure.stop').format(component.get('displayName')));
      });
    } else {
      return App.showConfirmationPopup(function () {
        var context = Em.I18n.t('requestInfo.stopHostComponent') + " " + component.get('displayName');
        self.sendComponentCommand(component, context, App.HostComponentStatus.stopped);
      }, Em.I18n.t('question.sure.stop').format(component.get('displayName')));
    }
  },
  /**
   * PUTs a command to server to start/stop a component. If no
   * specific component is provided, all components are started.
   * @param {object} component  When <code>null</code> all startable components are started.
   * @param {String} context  Context under which this command is beign sent.
   * @param {String} state - desired state of component can be 'STARTED' or 'STOPPED'
   * @method sendComponentCommand
   */
  sendComponentCommand: function (component, context, state) {
    var data = {
      hostName: this.get('content.hostName'),
      context: context,
      component: component,
      HostRoles: {
        state: state
      }
    };
    if (Array.isArray(component)) {
      data.query = "HostRoles/component_name.in(" + component.mapProperty('componentName').join(',') + ")";
    } else {
      data.componentName = component.get('componentName');
      data.serviceName = component.get('service.serviceName');
    }
    App.ajax.send({
      name: (Array.isArray(component)) ? 'common.host.host_components.update' : 'common.host.host_component.update',
      sender: this,
      data: data,
      success: 'sendComponentCommandSuccessCallback',
      error: 'ajaxErrorCallback',
      showLoadingPopup: true
    });
  },

  /**
   * Success callback for stop/start host component request
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method stopComponentSuccessCallback
   */
  sendComponentCommandSuccessCallback: function (data, opt, params) {
    var running = (params.HostRoles.state === App.HostComponentStatus.stopped) ? App.HostComponentStatus.stopping : App.HostComponentStatus.starting;
    params.component.set('workStatus', running);
    if (App.get('testMode')) {
      this.mimicWorkStatusChange(params.component, running, params.HostRoles.state);
    }
    this.showBackgroundOperationsPopup();
  },

  /**
   * Return true if hdfs user data is loaded via App.MainServiceInfoConfigsController
   */
  getHdfsUser: function () {
    var self = this;
    var dfd = $.Deferred();
    var miscController = App.MainAdminServiceAccountsController.create();
    miscController.loadUsers();
    miscController.addObserver('dataIsLoaded', this, function() {
      if (miscController.get('dataIsLoaded') && miscController.get('users')) {
        if (miscController.get('users').someProperty('name', 'hdfs_user')) {
          self.set('hdfsUser', miscController.get('users').findProperty('name', 'hdfs_user').get('value'));
        } else {
          self.set('hdfsUser', '&lt;hdfs-user&gt;');
        }
        dfd.resolve();
        miscController.destroy();
      }
    });
    return dfd.promise();
  },

  /**
   * this function will be called from :1) stop NN 2) restart NN 3) stop all components
   * @param callback - callback function to continue next operation
   * @param hostname - namenode host (by default is current host)
   */
  checkNnLastCheckpointTime: function(callback, hostName) {
    var self = this;
    this.pullNnCheckPointTime(hostName).then(function () {
      var isNNCheckpointTooOld = self.get('isNNCheckpointTooOld');
      self.set('isNNCheckpointTooOld', null);
      if (isNNCheckpointTooOld) {
        // too old
        self.getHdfsUser().done(function() {
          var msg = Em.Object.create({
            confirmMsg: Em.I18n.t('services.service.stop.HDFS.warningMsg.checkPointTooOld').format(App.nnCheckpointAgeAlertThreshold) +
              Em.I18n.t('services.service.stop.HDFS.warningMsg.checkPointTooOld.makeSure') +
              Em.I18n.t('services.service.stop.HDFS.warningMsg.checkPointTooOld.instructions.singleHost.login').format(isNNCheckpointTooOld) +
              Em.I18n.t('services.service.stop.HDFS.warningMsg.checkPointTooOld.instructions').format(self.get('hdfsUser')),
            confirmButton: Em.I18n.t('common.next')
          });
          return App.showConfirmationFeedBackPopup(callback, msg);
        });
      } else if (isNNCheckpointTooOld == null) {
        // not available
        return App.showConfirmationPopup(
          callback, Em.I18n.t('services.service.stop.HDFS.warningMsg.checkPointNA'), null,
          Em.I18n.t('common.warning'), Em.I18n.t('common.proceedAnyway'), 'danger'
        );
      } else {
        // still young
        callback();
      }
    });
  },

  pullNnCheckPointTime: function (hostName) {
    return App.ajax.send({
      name: 'common.host_component.getNnCheckPointTime',
      sender: this,
      data: {
        host: hostName || this.get('content.hostName')
      },
      success: 'parseNnCheckPointTime'
    });
  },

  parseNnCheckPointTime: function (data) {
    var lastCheckpointTime = Em.get(data, 'metrics.dfs.FSNamesystem.LastCheckpointTime');
    var hostName = Em.get(data, 'HostRoles.host_name');

    if (Em.get(data, 'metrics.dfs.FSNamesystem.HAState') === 'active') {
      if (!lastCheckpointTime) {
        this.set("isNNCheckpointTooOld", null);
      } else {
        var time_criteria = App.nnCheckpointAgeAlertThreshold; // time in hours to define how many hours ago is too old
        var time_ago = (Math.round(App.dateTime() / 1000) - (time_criteria * 3600)) *1000;
        if (lastCheckpointTime <= time_ago) {
          // too old, set the effected hostName
          this.set("isNNCheckpointTooOld", hostName);
        } else {
          // still young
          this.set("isNNCheckpointTooOld", false);
        }
      }
    } else if (Em.get(data, 'metrics.dfs.FSNamesystem.HAState') === 'standby') {
      this.set("isNNCheckpointTooOld", false);
    }
  },

  /**
   * mimic status transition in test mode
   * @param entity
   * @param transitionalState
   * @param finalState
   */
  mimicWorkStatusChange: function (entity, transitionalState, finalState) {
    if (Em.isArray(entity)) {
      entity.forEach(function (item) {
        item.set('workStatus', transitionalState);
        setTimeout(function () {
          item.set('workStatus', finalState);
        }, App.testModeDelayForActions);
      });
    } else {
      entity.set('workStatus', transitionalState);
      setTimeout(function () {
        entity.set('workStatus', finalState);
      }, App.testModeDelayForActions);
    }
  },

  /**
   * load data (if we need to show this background operations popup) from persist
   * @param callback
   */
  showBackgroundOperationsPopup: function (callback) {
    App.router.get('userSettingsController').dataLoading('show_bg').done(function (initValue) {
      if (initValue) {
        App.router.get('backgroundOperationsController').showPopup();
      }
      if (typeof callback === 'function') {
        callback();
      }
    });
  },

  /**
   * Send command to server to delete selected host component
   * @param {object} event
   * @method deleteComponent
   */
  deleteComponent: function (event) {
    if ($(event.target).closest('li').hasClass('disabled')) {
      return;
    }
    const component = event.context;
    const componentName = component.get('componentName');
    const componentsMapItem = this.get('addDeleteComponentsMap')[componentName];

    if (componentsMapItem) {
      this.deleteAndReconfigureComponent(componentsMapItem, component);
    } else if (componentName === 'JOURNALNODE') {
      return App.showConfirmationPopup(function () {
        App.router.transitionTo('main.services.manageJournalNode');
      }, Em.I18n.t('hosts.host.deleteComponent.popup.deleteJournalNodeMsg'));
    } else {
      return this.showReconfigurationPopupPreDelete(component, () => this._doDeleteHostComponent(componentName));
    }
  },

  /**
   *
   * @param {object} componentsMapItem
   * @param {App.HostComponent} component
   * @returns {App.ModalPopup}
   */
  deleteAndReconfigureComponent: function(componentsMapItem, component) {
    if (componentsMapItem.deletePropertyName) {
      this.set(componentsMapItem.deletePropertyName, true);
    }
    this.loadComponentRelatedConfigs(componentsMapItem.configTagsCallbackName, componentsMapItem.configsCallbackName);
    return this.showReconfigurationPopupPreDelete(component, () => {
      this._doDeleteHostComponent(component.get('componentName')).done(() => {
        this.applyConfigsCustomization();
        this.putConfigsToServer(this.get('groupedPropertiesToChange'), component.get('componentName'));
        this.clearConfigsChanges();
      });
    });
  },

  showReconfigurationPopupPreDelete: function (component, primary = Em.K, commonMessage) {
    const isLastComponent = (this.getTotalComponent(component) === 1),
      componentDisplayName = component.get('displayName');

    return App.ModalPopup.show({
      header: Em.I18n.t('popup.confirmation.commonHeader'),
      controller: this,
      hasPropertiesToChange: false,
      classNameBindings: ['controller.hasPropertiesToChange:common-modal-wrapper'],
      modalDialogClasses: function () {
        return this.get('controller.hasPropertiesToChange') ? ['modal-xlg'] : [];
      }.property('controller.hasPropertiesToChange'),
      primary: Em.I18n.t('hosts.host.deleteComponent.popup.confirm'),
      bodyClass: this.get('addDeleteComponentPopupBody').extend({
        commonMessage: commonMessage || Em.I18n.t('hosts.host.deleteComponent.popup.msg1').format(componentDisplayName),
        recommendedPropertiesToChange: this.get('recommendedPropertiesToChange'),
        requiredPropertiesToChange: this.get('requiredPropertiesToChange'),
        lastComponentError: Em.I18n.t('hosts.host.deleteComponent.popup.warning').format(componentDisplayName),
        lastComponent: isLastComponent
      }),
      isChecked: !isLastComponent,
      disablePrimary: function () {
        return (this.get('controller.isReconfigureRequired') && this.get('controller.isConfigsLoadingInProgress')) || !this.get('isChecked');
      }.property('controller.isReconfigureRequired', 'controller.isConfigsLoadingInProgress', 'isChecked'),
      onPrimary: function () {
        this._super();
        primary();
      },
      onSecondary: function () {
        this._super();
        this.get('controller').clearConfigsChanges();
      },
      onClose: function () {
        this._super();
        this.get('controller').clearConfigsChanges();
      }
    });
  },

  /**
   * get total count of host-components
   * @method getTotalComponent
   * @param component
   * @return {Number}
   */
  getTotalComponent: function (component) {
    var count;
    if (component.get('isSlave')) {
      count = App.SlaveComponent.find(component.get('componentName')).get('totalCount');
    } else if (component.get('isClient')) {
      count = App.ClientComponent.find(component.get('componentName')).get('totalCount');
    } else {
      count = App.HostComponent.find().filterProperty('componentName', component.get('componentName')).get('length');
    }
    return count || 0;
  },

  /**
   * Trigger to reset list of master/slaves components on the view
   * @type {bool}
   */
  redrawComponents: false,

  /**
   * Deletes the given host component, or all host components.
   *
   * @param {string} componentName
   * @return  {$.ajax}
   * @method _doDeleteHostComponent
   */
  _doDeleteHostComponent: function (componentName) {
    return App.ajax.send({
      name: (Em.isNone(componentName)) ? 'common.delete.host' : 'common.delete.host_component',
      sender: this,
      data: {
        componentName: componentName || '',
        hostName: this.get('content.hostName')
      },
      success: '_doDeleteHostComponentSuccessCallback',
      error: '_doDeleteHostComponentErrorCallback',
      showLoadingPopup: true
    });
  },

  /**
   * Error of delete component(s) request
   * @type {object}
   */
  _deletedHostComponentError: null,

  /**
   * Success callback for delete host component request
   * @method _doDeleteHostComponentSuccessCallback
   */
  _doDeleteHostComponentSuccessCallback: function (response, request, data) {
    this.set('_deletedHostComponentError', null);
  },

  /**
   * Error-callback for delete host component request
   * @param {object} xhr
   * @param {string} textStatus
   * @param {object} errorThrown
   * @param {object} data
   * @method _doDeleteHostComponentErrorCallback
   */
  _doDeleteHostComponentErrorCallback: function (xhr, textStatus, errorThrown, data) {
    this.set('_deletedHostComponentError', {xhr: xhr, url: data.url, method: 'DELETE'});
  },

  /**
   * Send command to server to upgrade selected host component
   * @param {object} event
   * @method upgradeComponent
   */
  upgradeComponent: function (event) {
    var self = this;
    var component = event.context;
    return App.showConfirmationPopup(function () {
      App.ajax.send({
        name: 'host.host_component.upgrade',
        sender: self,
        data: {
          component: component,
          hostName: self.get('content.hostName'),
          componentName: component.get('componentName'),
          data: JSON.stringify({
            RequestInfo: {
              "context": Em.I18n.t('requestInfo.upgradeHostComponent') + " " + component.get('displayName')
            },
            Body: {
              HostRoles: {
                stack_id: 'HDP-1.2.2',
                state: 'INSTALLED'
              }
            }
          })
        },
        success: 'upgradeComponentSuccessCallback',
        error: 'ajaxErrorCallback'
      });
    }, Em.I18n.t('question.sure.upgrade').format(component.get('displayName')));
  },

  /**
   * Success callback for upgrade host component request
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method upgradeComponentSuccessCallback
   */
  upgradeComponentSuccessCallback: function (data, opt, params) {
    if (App.get('testMode')) {
      this.mimicWorkStatusChange(params.component, App.HostComponentStatus.starting, App.HostComponentStatus.started);
    }
    this.showBackgroundOperationsPopup();
  },

  /**
   * Send command to server to restart selected components
   * @param {object} event
   * @method restartComponent
   */
  restartComponent: function (event) {
    var component = event.context;
    if (event.context.get('componentName') === 'NAMENODE') {
      this.checkNnLastCheckpointTime(function () {
        return App.showConfirmationPopup(function () {
          batchUtils.restartHostComponents([component], Em.I18n.t('rollingrestart.context.selectedComponentOnSelectedHost').format(component.get('displayName')), "HOST_COMPONENT");
        }, Em.I18n.t('question.sure.restart').format(component.get('displayName')));
      });
    } else {
      return App.showConfirmationPopup(function () {
        batchUtils.restartHostComponents([component], Em.I18n.t('rollingrestart.context.selectedComponentOnSelectedHost').format(component.get('displayName')), "HOST_COMPONENT");
      }, Em.I18n.t('question.sure.restart').format(component.get('displayName')));
    }
  },

  /**
   * add component as <code>addComponent<code> method but perform
   * kdc session state if cluster is secure;
   * @param event
   */
  addComponentWithCheck: function (event) {
    App.get('router.mainAdminKerberosController').getSecurityType(function (event) {
      App.get('router.mainAdminKerberosController').getKDCSessionState(this.addComponent.bind(this, event));
    }.bind(this, event));
  },
  /**
   * Send command to server to install selected host component
   * @param {object} event
   * @method addComponent
   */
  addComponent: function (event) {
    var
      component = event.context,
      hostName = this.get('content.hostName'),
      componentName = component.get('componentName'),
      missedComponents = event.fromServiceSummary ? [] : this.checkComponentDependencies(componentName, {
        scope: 'host',
        installedComponents: this.get('content.hostComponents').mapProperty('componentName')
      }),
      isManualKerberos = App.get('router.mainAdminKerberosController.isManualKerberos'),
      manualKerberosWarning = isManualKerberos ? Em.I18n.t('hosts.host.manualKerberosWarning') : '',
      componentsMapItem = this.get('addDeleteComponentsMap')[componentName];

    if (missedComponents.length) {
      const popupMessage = Em.I18n.t('host.host.addComponent.popup.dependedComponents.body').format(component.get('displayName'),
        stringUtils.getFormattedStringFromArray(missedComponents.map(function (cName) {
          return App.StackServiceComponent.find(cName).get('displayName');
        })));
      return App.showAlertPopup(Em.I18n.t('host.host.addComponent.popup.dependedComponents.header'), popupMessage);
    }

    if (componentsMapItem) {
      this.addAndReconfigureComponent(componentsMapItem, hostName, component, event.fromServiceSummary);
    } else if (componentName === 'JOURNALNODE') {
      return App.showConfirmationPopup(function () {
        App.router.transitionTo('main.services.manageJournalNode');
      }, Em.I18n.t('hosts.host.addComponent.' + componentName) + manualKerberosWarning);
    } else {
      return this.showAddComponentPopup(component, hostName, () => {
        // hostname should be taken via this.get('content.hostName') because the host could be selected later
        this.installHostComponentCall(this.get('content.hostName'), component);
      }, event.fromServiceSummary);
    }
  },

  /**
   *
   * @param {object} componentsMapItem
   * @param {string} hostName
   * @param {App.HostComponent} component
   * @param {boolean} fromServiceSummary
   * @returns {*}
   */
  addAndReconfigureComponent: function(componentsMapItem, hostName, component, fromServiceSummary) {
    if (componentsMapItem.hostPropertyName) {
      this.set(componentsMapItem.hostPropertyName, hostName);
    }
    if (componentsMapItem.addPropertyName) {
      this.set(componentsMapItem.addPropertyName, true);
    }
    this.loadComponentRelatedConfigs(componentsMapItem.configTagsCallbackName, componentsMapItem.configsCallbackName);
    return this.showAddComponentPopup(component, hostName, () => {
      this.installAndReconfigureComponent(this.get('content.hostName'), component, componentsMapItem);
    }, fromServiceSummary);
  },

  /**
   *
   * @param {string} hostName
   * @param {App.HostComponent} component
   * @param {object} componentsMapItem
   */
  installAndReconfigureComponent: function(hostName, component, componentsMapItem) {
    this.applyConfigsCustomization();
    this.installHostComponentCall(hostName, component).done(() => {
      this.putConfigsToServer(this.get('groupedPropertiesToChange'), component.get('componentName'));
    }).always(() => {
      this.set(componentsMapItem.addPropertyName, false);
      this.clearConfigsChanges();
    });
  },

  showAddComponentPopup: function (component, hostName, primary, fromServiceSummary) {
    var self = this,
      componentName = component.get('componentName'),
      componentDisplayName = component.get('displayName'),
      componentsMapItem = this.get('addDeleteComponentsMap')[componentName],
      commonMessage = Em.I18n.t('hosts.host.addComponent.msg').format(componentDisplayName);

    return App.ModalPopup.show({
      header: Em.I18n.t('popup.confirmation.commonHeader'),
      controller: self,
      hasPropertiesToChange: false,
      classNameBindings: ['hasPropertiesToChange:common-modal-wrapper'],
      modalDialogClasses: function () {
        return this.get('controller.hasPropertiesToChange') ? ['modal-xlg'] : [];
      }.property('controller.hasPropertiesToChange'),
      primary: Em.I18n.t('hosts.host.addComponent.popup.confirm'),
      bodyClass: self.get('addDeleteComponentPopupBody').extend({
        commonMessage: commonMessage,
        fromServiceSummary: fromServiceSummary,
        addComponentMsg: Em.I18n.t('hosts.host.addComponent.msg').format(componentDisplayName),
        selectHostMsg: Em.I18n.t('services.summary.selectHostForComponent').format(componentDisplayName),
        thereIsNoHostsMsg: Em.I18n.t('services.summary.allHostsAlreadyRunComponent').format(componentDisplayName),
        hostsWithoutComponent: function () {
          if (this.get('fromServiceSummary')) {
            const hostsWithComponent = App.HostComponent.find().filterProperty('componentName', componentName).mapProperty('hostName');
            return App.get('allHostNames').filter((hostname) => !hostsWithComponent.contains(hostname));
          } else {
            return [];
          }
        }.property('fromServiceSummary'),
        anyHostsWithoutComponent: Em.computed.or('!fromServiceSummary', 'hostsWithoutComponent.length'),
        selectedHostObserver: function () {
          const selectedHostName = this.get('selectedHost');
          if (!self.get('content')) {
            self.set('content', {});
          }
          self.setProperties({
            'content.hostName': selectedHostName
          });
          if (componentsMapItem) {
            const configs = self.get('configs');
            const params = configs && configs.params || {};
            if (componentsMapItem.hostPropertyName) {
              self.set(componentsMapItem.hostPropertyName, selectedHostName);
            }
            if (componentsMapItem.addPropertyName) {
              self.set(componentsMapItem.addPropertyName, true);
            }
            if (configs) {
              self.clearConfigsChanges(true);
              self.set('isReconfigureRequired', true);
              self.set('isConfigsLoadingInProgress', true);
              self[componentsMapItem.configsCallbackName](configs, null, params);
            } else {
              self.loadComponentRelatedConfigs(componentsMapItem.configTagsCallbackName, componentsMapItem.configsCallbackName);
            }
          }
        }.observes('selectedHost')
      }),
      disablePrimary: Em.computed.and('controller.isReconfigureRequired', 'controller.isConfigsLoadingInProgress'),
      onPrimary: function () {
        this._super();
        if (primary) {
          primary();
        }
      },
      onSecondary: function () {
        this._super();
        self.clearConfigsChanges();
      },
      onClose: function () {
        this._super();
        self.clearConfigsChanges();
      }
    });
  },

  loadComponentRelatedConfigs: function (configTagsCallbackName, configsCallbackName) {
    this.clearConfigsChanges();
    this.set('isReconfigureRequired', true);
    this.set('isConfigsLoadingInProgress', true);
    this.isServiceMetricsLoaded(() => {
      this.loadConfigs(configTagsCallbackName, configsCallbackName);
    });
  },

  /**
   * Success callback for install host component request (sent in <code>addNewComponentSuccessCallback</code>)
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method installNewComponentSuccessCallback
   */
  installNewComponentSuccessCallback: function (data, opt, params) {
    if(typeof data==="string"){
      data=JSON.parse(data)
    }
    if (!data || !data.Requests || !data.Requests.id) {
      return false;
    }

    if (App.get('testMode')) {
      this.mimicWorkStatusChange(params.component, App.HostComponentStatus.installing, App.HostComponentStatus.stopped);
    }

    this.showBackgroundOperationsPopup();
    return true;
  },

  /**
   * Call <code>setRackInfo</code> function to show Set Rack Id popup
   * @param data
   */
  setRackId: function (data) {
    var rack = data.context.get('rack');
    var hosts = [data.context];
    var operationData = {message: Em.I18n.t('hosts.host.details.setRackId')};
    hostsManagement.setRackInfo(operationData, hosts, rack);
  },

  /**
   * Success callback for load configs request
   * @param {object} data
   * @method loadOozieConfigs
   */
  loadOozieConfigs: function (data) {
    return App.ajax.send({
      name: 'admin.get.all_configurations',
      sender: this,
      data: {
        urlParams: '(type=oozie-env&tag=' + data.Clusters.desired_configs['oozie-env'].tag + ')'
      },
      success: 'onLoadOozieConfigs',
      error: 'onLoadConfigsErrorCallback'
    });
  },

  /**
   * get Oozie database config and set databaseType
   * @param {object} data
   * @method onLoadOozieConfigs
   */
  onLoadOozieConfigs: function (data) {
    var configs = {};
    data.items.forEach(function (item) {
      $.extend(configs, item.properties);
    });
    this.set('isOozieServerAddable', !(Em.isEmpty(configs["oozie_database"]) || configs["oozie_database"] === 'New Derby Database'));
    this.get('isOozieConfigLoaded').resolve();
  },


  /**
   * Success callback for Storm load configs request
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method loadStormConfigs
   */
  loadStormConfigs: function (data, opt, params) {
    var request = App.ajax.send({
      name: 'admin.get.all_configurations',
      sender: this,
      data: {
        urlParams: '(type=storm-site&tag=' + data.Clusters.desired_configs['storm-site'].tag + ')'
      },
      success: params.callback
    });
    this.trackRequest(request);
  },

  /**
   * Success callback for Atlas load configs request
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method loadAtlasConfigs
   */
  loadAtlasConfigs: function (data, opt, params) {
    var request = App.ajax.send({
      name: 'admin.get.all_configurations',
      sender: this,
      data: {
        urlParams: '(type=application-properties&tag=' + data.Clusters.desired_configs['application-properties'].tag + ')'
      },
      success: params.callback
    });
    this.trackRequest(request);
  },

  /**
   * Update zk configs
   * @param {object} configs
   * @method updateZkConfigs
   */
  updateZkConfigs: function (configs) {
    const portValue = configs['zoo.cfg'] && Em.get(configs['zoo.cfg'], 'clientPort'),
      zkPort = typeof portValue === 'undefined' ? '2181' : portValue,
      infraSolrZnode = configs['infra-solr-env'] ? Em.get(configs['infra-solr-env'], 'infra_solr_znode') : '/ambari-solr',
      initializer = App.AddZooKeeperComponentsInitializer,
      hostComponentsTopology = {
        masterComponentHosts: []
      },
      propertiesToChange = this.get('allPropertiesToChange'),
      masterComponents = this.bootstrapHostsMapping('ZOOKEEPER_SERVER');
    if (this.get('fromDeleteHost') || this.get('fromDeleteZkServer')) {
      this.set('fromDeleteHost', false);
      this.set('fromDeleteZkServer', false);
      let removedHost = masterComponents.findProperty('hostName', this.get('content.hostName'));
      if (!Em.isNone(removedHost)) {
        Em.set(removedHost, 'isInstalled', false);
      }
    } else if (this.get('addZooKeeperServer')) {
      this.set('addZooKeeperServer', false);
      masterComponents.push({
        component: 'ZOOKEEPER_SERVER',
        hostName: this.get('content.hostName'),
        isInstalled: true
      });
    }
    const dependencies = {
      zkClientPort: zkPort,
      infraSolrZnode
    };
    hostComponentsTopology.masterComponentHosts = masterComponents;
    Em.keys(configs).forEach(fileName => {
      const properties = configs[fileName];
      Em.keys(properties).forEach(propertyName => {
        const currentValue = properties[propertyName],
          propertyDef = {
            fileName,
            name: propertyName,
            value: currentValue
          },
          configProperty = initializer.initialValue(propertyDef, hostComponentsTopology, dependencies);
        initializer.updateSiteObj(configs[fileName], configProperty);
        if (currentValue !== configs[fileName][propertyName]) {
          const service = App.config.get('serviceByConfigTypeMap')[fileName],
            configObject = App.configsCollection.getConfigByName(propertyName, fileName),
            displayName = configObject && configObject.displayName;
          propertiesToChange.pushObject({
            propertyFileName: fileName,
            propertyName,
            propertyTitle: configObject && Em.I18n.t('installer.controls.serviceConfigPopover.title').format(displayName, displayName === propertyName ? '' : propertyName),
            propertyDescription: configObject && configObject.description,
            serviceDisplayName: service && service.get('displayName'),
            initialValue: currentValue,
            recommendedValue: propertyDef.value
          });
        }
      });
    });
  },

  /**
   *
   * @param {string} componentName
   * @param {string[]} [hostNames]
   * @returns {}
   */
  bootstrapHostsMapping: function(componentName, hostNames) {
    if (Em.isNone(hostNames)) {
      hostNames = App.HostComponent.find().filterProperty('componentName', componentName).mapProperty('hostName');
    }
    return hostNames.map(function(hostName) {
      return {
        component: componentName,
        hostName: hostName,
        isInstalled: true
      };
    });
  },

  /**
   * update and save Storm related configs to server
   * @param {object} data
   * @method onLoadStormConfigs
   */
  onLoadStormConfigs: function (data) {
    const nimbusHost = this.get('nimbusHost'),
      stormNimbusHosts = this.getStormNimbusHosts(),
      configs = {},
      attributes = {},
      propertiesToChange = this.get('allPropertiesToChange');

    this.saveLoadedConfigs(data);
    data.items.forEach(item => {
      configs[item.type] = item.properties;
      attributes[item.type] = item.properties_attributes || {};
    });

    this.updateZkConfigs(configs);

    const propertyName = 'nimbus.seeds',
      propertyFileName = 'storm-site',
      nimbusSeedsInit = configs[propertyFileName][propertyName],
      nimbusSeedsRecommended = JSON.stringify(stormNimbusHosts).replace(/"/g, "'");
    configs[propertyFileName][propertyName] = nimbusSeedsRecommended;
    if (this.get('isReconfigureRequired') && nimbusSeedsInit !== nimbusSeedsRecommended) {
      const service = App.config.get('serviceByConfigTypeMap')[propertyFileName],
        configObject = App.configsCollection.getConfigByName(propertyName, propertyFileName),
        displayName = configObject && configObject.displayName;
      propertiesToChange.pushObject({
        propertyFileName,
        propertyName,
        propertyTitle: configObject && Em.I18n.t('installer.controls.serviceConfigPopover.title').format(displayName, displayName === propertyName ? '' : propertyName),
        propertyDescription: configObject && configObject.description,
        serviceDisplayName: service && service.get('displayName'),
        initialValue: nimbusSeedsInit,
        recommendedValue: nimbusSeedsRecommended
      });
    }
    const groups = [
      {
        properties: {
          [propertyFileName]: configs[propertyFileName]
        },
        properties_attributes: {
          [propertyFileName]: attributes[propertyFileName]
        }
      }
    ];
    this.setConfigsChanges(groups);
  },

  onLoadAtlasConfigs: function(data) {
    const atlasServer = this.get('atlasServer'),
      atlasServerHosts = this.getAtlasServerHosts(),
      configs = {},
      attributes = {},
      propertiesToChange = this.get('allPropertiesToChange');

    this.saveLoadedConfigs(data);
    data.items.forEach(item => {
      configs[item.type] = item.properties;
      attributes[item.type] = item.properties_attributes || {};
    });

    const propertyFileName = 'application-properties',
      propertyName = 'atlas.rest.address',
      atlasAddresses = configs[propertyFileName][propertyName],
      hostMask = atlasAddresses.split(',')[0].replace(/([https|http]*\:\/\/)(.*?)(:[0-9]+)/, '$1{hostname}$3'),
      atlasAddressesRecommended = atlasServerHosts.map(hostName => hostMask.replace('{hostname}', hostName)).join(',');
    configs[propertyFileName][propertyName] = atlasAddressesRecommended;
    if (this.get('isReconfigureRequired') && atlasAddresses !== atlasAddressesRecommended) {
      var service = App.config.get('serviceByConfigTypeMap')[propertyFileName],
        configObject = App.configsCollection.getConfigByName(propertyName, propertyFileName),
        displayName = configObject && configObject.displayName;
      propertiesToChange.pushObject({
        propertyFileName,
        propertyName,
        propertyTitle: configObject && Em.I18n.t('installer.controls.serviceConfigPopover.title').format(displayName, displayName === propertyName ? '' : propertyName),
        propertyDescription: configObject && configObject.description,
        serviceDisplayName: service && service.get('displayName'),
        initialValue: atlasAddresses,
        recommendedValue: atlasAddressesRecommended
      });
    }
    const groups = [
      {
        properties: {
          [propertyFileName]: configs[propertyFileName]
        },
        properties_attributes: {
          [propertyFileName]: attributes[propertyFileName]
        }
      }
    ];
    this.setConfigsChanges(groups);
  },

  /**
   * Success callback for load configs request
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method loadWebHCatConfigs
   */
  loadWebHCatConfigs: function (data, opt, params) {
    const urlParams = this.getUrlParamsForConfigsRequest(data, ['hive-site', 'webhcat-site', 'hive-env', 'core-site']),
      request = App.ajax.send({
        name: 'admin.get.all_configurations',
        sender: this,
        data: {
          webHCat: true,
          urlParams
        },
        success: params.callback
      });
    this.trackRequest(request);
    return request;
  },

  /**
   * Success callback for load configs request
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method loadHiveConfigs
   */
  loadHiveConfigs: function (data, opt, params) {
    const urlParams = this.getUrlParamsForConfigsRequest(data, ['hive-site', 'webhcat-site', 'hive-env', 'core-site']),
      request = App.ajax.send({
        name: 'admin.get.all_configurations',
        sender: this,
        data: {
          urlParams
        },
        success: params.callback
      });
    this.trackRequest(request);
    return request;
  },

  /**
   * update and save Hive related configs to server
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method onLoadHiveConfigs
   */
  onLoadHiveConfigs: function (data, opt, params) {
    let port = "";
    const configs = {},
      attributes = {},
      userSetup = {},
      localDB = {
        masterComponentHosts: this.getHiveHosts()
      },
      dependencies = {
        hiveMetastorePort: ""
      },
      initializer = params.webHCat ? App.AddWebHCatComponentsInitializer : App.AddHiveComponentsInitializer;
    this.saveLoadedConfigs(data);
    this.set('configs.params', {
      webHCat: params.webHCat
    });
    data.items.forEach(item => {
      configs[item.type] = item.properties;
      attributes[item.type] = item.properties_attributes || {};
    });
    const propertiesToChange = this.get('allPropertiesToChange');

    port = configs['hive-site']['hive.metastore.uris'].match(/:[0-9]{2,4}/);
    port = port ? port[0].slice(1) : "9083";

    dependencies.hiveMetastorePort = port;

    if (params.webHCat) {
      userSetup.webhcatUser = configs['hive-env']['webhcat_user'];
    } else {
      userSetup.hiveUser = configs['hive-env']['hive_user'];
    }

    initializer.setup(userSetup);

    ['hive-site', 'webhcat-site', 'hive-env', 'core-site'].forEach(fileName => {
      if (configs[fileName]) {
        Em.keys(configs[fileName]).forEach(propertyName => {
          const currentValue = configs[fileName][propertyName],
            propertyDef = {
              fileName,
              name: propertyName,
              value: currentValue
            },
            configProperty = initializer.initialValue(propertyDef, localDB, dependencies);
          initializer.updateSiteObj(configs[fileName], configProperty);
          if (this.get('isReconfigureRequired') && currentValue !== configs[fileName][propertyName]) {
            const service = App.config.get('serviceByConfigTypeMap')[fileName],
              configObject = App.configsCollection.getConfigByName(propertyName, fileName),
              displayName = configObject && configObject.displayName;
            propertiesToChange.pushObject({
              propertyFileName: fileName,
              propertyName,
              propertyTitle: configObject && Em.I18n.t('installer.controls.serviceConfigPopover.title').format(displayName, displayName === propertyName ? '' : propertyName),
              propertyDescription: configObject && configObject.description,
              serviceDisplayName: service && service.get('displayName'),
              initialValue: currentValue,
              recommendedValue: propertyDef.value
            });
          }
        });
      }
    });

    initializer.cleanup();

    var groups = [
      {
        properties: {
          'hive-site': configs['hive-site'],
          'webhcat-site': configs['webhcat-site'],
          'hive-env': configs['hive-env']
        },
        properties_attributes: {
          'hive-site': attributes['hive-site'],
          'webhcat-site': attributes['webhcat-site'],
          'hive-env': attributes['hive-env']
        }
      },
      {
        properties: {
          'core-site': configs['core-site']
        },
        properties_attributes: {
          'core-site': attributes['core-site']
        }
      }
    ];
    this.setConfigsChanges(groups);
  },

  /**
   * save configs' sites in batch
   * @param groups
   * @param componentName
   */
  putConfigsToServer: function (groups, componentName) {
    const dfd = $.Deferred();
    const requests = [];
    if (groups.length) {
      groups.forEach(function (group) {
        const desiredConfigs = [];
        const properties = group.properties;

        for (let site in properties) {
          if (!properties.hasOwnProperty(site) || Em.isNone(properties[site])) continue;
          desiredConfigs.push({
            "type": site,
            "properties": properties[site],
            "properties_attributes": group.properties_attributes[site],
            "service_config_version_note": Em.I18n.t('hosts.host.configs.save.note').format(App.format.role(componentName, false))
          });
        }
        if (desiredConfigs.length > 0) {
          requests.push(App.ajax.send({
            name: 'common.service.configurations',
            sender: this,
            data: {
              desired_config: desiredConfigs,
              componentName: componentName
            }
          }));
        }
      }, this);
      $.when(requests).done(dfd.resolve).fail(dfd.reject).then(() => {
        // If user adding component which require config changes from Configs page then configs should be reloaded
        if (App.router.get('location.location.hash').contains('configs')) {
          App.router.get('mainServiceInfoConfigsController').loadStep();
        }
      });
    } else {
      dfd.resolve();
    }
    return dfd.promise();
  },

  /**
   * Delete Hive Metastore is performed
   * @type {bool}
   */
  deleteHiveMetaStore: false,

  /**
   * Delete WebHCat Server is performed
   *
   * @type {bool}
   */
  deleteWebHCatServer: false,

  getHiveHosts: function () {
    var self = this;
    var removePerformed = this.get('fromDeleteHost') || this.get('deleteHiveMetaStore') || this.get('deleteHiveServer') || this.get('deleteWebHCatServer');
    var hiveMasterComponents = ['WEBHCAT_SERVER', 'HIVE_METASTORE', 'HIVE_SERVER'];
    var masterComponentsMap = hiveMasterComponents.map(function(componentName) {
      return self.bootstrapHostsMapping(componentName);
    }).reduce(function(p,c) {
      return p.concat(c);
    });

    if (removePerformed) {
      self.setProperties({
        deleteHiveMetaStore: false,
        deleteHiveServer: false,
        deleteWebHCatServer: false,
        fromDeleteHost: false
      });
      masterComponentsMap = masterComponentsMap.map(function(masterComponent) {
        masterComponent.isInstalled = masterComponent.hostName !== self.get('content.hostName');
        return masterComponent;
      });
    }

    if (!!this.get('hiveMetastoreHost')) {
      masterComponentsMap.push({
        component: 'HIVE_METASTORE',
        hostName: this.get('hiveMetastoreHost'),
        isInstalled: !removePerformed
      });
      this.set('hiveMetastoreHost', '');
    }

    if (!!this.get('webhcatServerHost')) {
      masterComponentsMap.push({
        component: 'WEBHCAT_SERVER',
        hostName: this.get('webhcatServerHost'),
        isInstalled: !removePerformed
      });
      this.set('webhcatServerHost', '');
    }

    return masterComponentsMap;
  },

  /**
   * Success callback for load configs request
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method loadRangerConfigs
   */
  loadRangerConfigs: function (data, opt, params) {
    const urlParams = this.getUrlParamsForConfigsRequest(data, ['core-site', 'hdfs-site', 'kms-env', 'kms-site']),
      request = App.ajax.send({
        name: 'admin.get.all_configurations',
        sender: this,
        data: {
          urlParams
        },
        success: params.callback
      });
    this.trackRequest(request);
  },

  /**
   * update and save Hive hive.metastore.uris config to server
   * @param {object} data
   * @method onLoadRangerConfigs
   */
  onLoadRangerConfigs: function (data) {
    const hdfsProperties = [
        {
          type: 'core-site',
          name: 'hadoop.security.key.provider.path'
        },
        {
          type: 'hdfs-site',
          name: 'dfs.encryption.key.provider.uri'
        }
      ],
      kmsSiteProperties = [
        {
          name: 'hadoop.kms.cache.enable',
          notHaValue: 'true',
          haValue: 'false'
        },
        {
          name: 'hadoop.kms.authentication.zk-dt-secret-manager.enable',
          notHaValue: 'false',
          haValue: 'true'
        },
        {
          name: 'hadoop.kms.cache.timeout.ms',
          notHaValue: '600000',
          haValue: '0'
        },
        {
          name: 'hadoop.kms.current.key.cache.timeout.ms',
          notHaValue: '30000',
          haValue: '0'
        },
        {
          name: 'hadoop.kms.authentication.signer.secret.provider',
          notHaValue: 'random',
          haValue: 'zookeeper'
        },
        {
          name: 'hadoop.kms.authentication.signer.secret.provider.zookeeper.auth.type',
          notHaValue: 'kerberos',
          haValue: 'none'
        },
        {
          name: 'hadoop.kms.authentication.signer.secret.provider.zookeeper.connection.string',
          notHaValue: '#HOSTNAME#:#PORT#,...',
          haValue: this.getZookeeperConnectionString()
        }
      ],
      rkmsHosts = this.getRangerKMSServerHosts(),
      rkmsHostsStr = rkmsHosts.join(';'),
      isHA = rkmsHosts.length > 1,
      rkmsPort = data.items.findProperty('type', 'kms-env').properties['kms_port'],
      newValue = 'kms://http@' + rkmsHostsStr + ':' + rkmsPort + '/kms',
      coreSiteConfigs = data.items.findProperty('type', 'core-site'),
      hdfsSiteConfigs = data.items.findProperty('type', 'hdfs-site'),
      kmsSiteConfigs = data.items.findProperty('type', 'kms-site'),
      groups = [
        {
          properties: {
            'core-site': coreSiteConfigs.properties,
            'hdfs-site': hdfsSiteConfigs.properties
          },
          properties_attributes: {
            'core-site': coreSiteConfigs.properties_attributes,
            'hdfs-site': hdfsSiteConfigs.properties_attributes
          }
        },
        {
          properties: {
            'kms-site': kmsSiteConfigs.properties
          },
          properties_attributes: {
            'kms-site': kmsSiteConfigs.properties_attributes
          }
        }
      ],
      propertiesToChange = this.get('allPropertiesToChange');

    this.saveLoadedConfigs(data);

    hdfsProperties.forEach(property => {
      const typeConfigs = data.items.findProperty('type', property.type).properties,
        currentValue = typeConfigs[property.name],
        pattern = new RegExp('^kms:\\/\\/http@(.+):' + rkmsPort + '\\/kms$'),
        patternMatch = currentValue && currentValue.match(pattern),
        currentHostsList = patternMatch && patternMatch[1].split(';').sort().join(';');
      if (currentHostsList !== rkmsHostsStr) {
        typeConfigs[property.name] = newValue;
        if (this.get('isReconfigureRequired')) {
          const propertyFileName = property.type,
            propertyName = property.name,
            service = App.config.get('serviceByConfigTypeMap')[propertyFileName],
            configObject = App.configsCollection.getConfigByName(propertyName, propertyFileName),
            displayName = configObject && configObject.displayName;
          propertiesToChange.pushObject({
            propertyFileName,
            propertyName,
            propertyTitle: configObject && Em.I18n.t('installer.controls.serviceConfigPopover.title').format(displayName, displayName === propertyName ? '' : propertyName),
            propertyDescription: configObject && configObject.description,
            serviceDisplayName: service && service.get('displayName'),
            initialValue: currentValue,
            recommendedValue: newValue,
            saveRecommended: true
          });
        }
      }
    });

    kmsSiteProperties.forEach(function (property) {
      var currentValue = kmsSiteConfigs.properties[property.name];
      var newValue = isHA ? property.haValue : property.notHaValue;
      kmsSiteConfigs.properties[property.name] = newValue;

      propertiesToChange.pushObject({
        propertyFileName: 'kms-site',
        propertyName: property.name,
        serviceDisplayName: App.Service.find().findProperty('serviceName', 'RANGER_KMS').get('displayName'),
        initialValue: currentValue,
        recommendedValue: newValue,
        saveRecommended: true
      });
    });
    this.setConfigsChanges(groups);
  },

  /**
   * Delete Hive Metastore is performed
   * @type {bool}
   */
  deleteRangerKMSServer: false,

  getRangerKMSServerHosts: function () {
    var rkmsHosts = App.HostComponent.find().filterProperty('componentName', 'RANGER_KMS_SERVER').mapProperty('hostName');
    var rangerKMSServerHost = this.get('rangerKMSServerHost');

    if (!!rangerKMSServerHost) {
      rkmsHosts.push(rangerKMSServerHost);
    }

    if (this.get('fromDeleteHost') || this.get('deleteRangerKMSServer')) {
      return rkmsHosts.without(this.get('content.hostName'));
    }
    return rkmsHosts.sort();
  },

  getZookeeperConnectionString: function () {
    var zookeeperHosts = App.MasterComponent.find('ZOOKEEPER_SERVER').get('hostNames');
    return zookeeperHosts.map(function (host) {
      return host + ':2181';
    }).join(',');
  },

  /**
   * Delete Storm Nimbus is performed
   * @type {bool}
   */
  deleteNimbusHost: false,

  getStormNimbusHosts: function () {
    var
      stormNimbusHosts = App.HostComponent.find().filterProperty('componentName', 'NIMBUS').mapProperty('hostName'),
      nimbusHost = this.get('nimbusHost');

    if (!!nimbusHost) {
      stormNimbusHosts.push(nimbusHost);
      this.set('nimbusHost', '');
    }

    if (this.get('fromDeleteHost') || this.get('deleteNimbusHost')) {
      this.set('deleteNimbusHost', false);
      this.set('fromDeleteHost', false);
      return stormNimbusHosts.without(this.get('content.hostName'));
    }
    return stormNimbusHosts.sort();
  },

  getAtlasServerHosts: function () {
    var
      atlasServerHosts = App.HostComponent.find().filterProperty('componentName', 'ATLAS_SERVER').mapProperty('hostName'),
      atlasServer = this.get('atlasServer');

    if (!!atlasServer) {
      atlasServerHosts.push(atlasServer);
      this.set('atlasServer', '');
    }

    if (this.get('fromDeleteHost') || this.get('deleteAtlasServer')) {
      this.set('deleteAtlasServer', false);
      this.set('fromDeleteHost', false);
      return atlasServerHosts.without(this.get('content.hostName'));
    }
    return atlasServerHosts.sort();
  },

  /**
   * Send command to server to resfresh configs of selected component
   * @param {object} event
   * @method refreshComponentConfigs
   */
  refreshComponentConfigs: function (event) {
    var self = this;
    return App.showConfirmationPopup(function () {
      var component = event.context;
      var context = Em.I18n.t('requestInfo.refreshComponentConfigs').format(component.get('displayName'));
      self.sendRefreshComponentConfigsCommand(component, context);
    });
  },

  /**
   * PUTs a command to server to refresh configs of host component.
   * @param {object} component
   * @param {object} context Context under which this command is beign sent.
   * @method sendRefreshComponentConfigsCommand
   */
  sendRefreshComponentConfigsCommand: function (component, context) {
    var resource_filters = [
      {
        service_name: component.get('service.serviceName'),
        component_name: component.get('componentName'),
        hosts: component.get('host.hostName')
      }
    ];
    App.ajax.send({
      name: 'host.host_component.refresh_configs',
      sender: this,
      data: {
        resource_filters: resource_filters,
        context: context
      },
      success: 'refreshComponentConfigsSuccessCallback',
      showLoadingPopup: true
    });
  },

  /**
   * Success callback for refresh host component configs request
   * @method refreshComponentConfigsSuccessCallback
   */
  refreshComponentConfigsSuccessCallback: function () {
    this.showBackgroundOperationsPopup();
  },

  /**
   * Load configs
   * This function when used without a callback should be always used from successcallback function of the promise `App.router.get('mainController').isLoading.call(App.router.get('clusterController'), 'isServiceContentFullyLoaded').done(promise)`
   * This is required to make sure that service metrics API determining the HA state of components is loaded
   * @method loadConfigs
   */
  loadConfigs: function (configTagsCallback, configsCallback) {
    var request = App.ajax.send({
      name: 'config.tags',
      sender: this,
      data: {
        callback: configsCallback
      },
      success: configTagsCallback,
      error: 'onLoadConfigsErrorCallback'
    });
    this.trackRequest(request);
  },

  /**
   * onLoadConfigsErrorCallback
   * @method onLoadConfigsErrorCallback
   */
  onLoadConfigsErrorCallback: function () {
    this.get('isOozieConfigLoaded').reject();
  },

  /**
   * Success callback for load configs request
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method loadZookeeperConfigs
   */
  loadZookeeperConfigs: function (data, opt, params) {
    var urlParams = this.constructZookeeperConfigUrlParams(data);
    if (urlParams.length > 0) {
      var request = App.ajax.send({
        name: 'reassign.load_configs',
        sender: this,
        data: {
          urlParams: urlParams.join('|')
        },
        success: params.callback
      });
      this.trackRequest(request);
      return true;
    }
    this.set('isConfigsLoadingInProgress', false);
    return false;
  },

  /**
   * construct URL params for query, that load configs
   * @param data {Object}
   * @return {Array}
   */
  constructZookeeperConfigUrlParams: function (data) {
    var urlParams = [];
    var services = App.Service.find();
    var zooKeeperRelatedServices = this.get('zooKeeperRelatedServices').slice(0);
    if (App.get('isHaEnabled')) {
      zooKeeperRelatedServices.push({
        serviceName: 'HDFS',
        typesToLoad: ['core-site'],
        typesToSave: ['core-site']
      });
    }
    zooKeeperRelatedServices.forEach(function (service) {
      if (services.someProperty('serviceName', service.serviceName)) {
        service.typesToLoad.forEach(function (type) {
          if (data.Clusters.desired_configs[type]) {
            urlParams.push('(type=' + type + '&tag=' + data.Clusters.desired_configs[type].tag + ')');
          }
        });
      }
    });
    return urlParams;
  },

  /**
   * save new ZooKeeper configs to server
   * @param {object} data
   * @method saveZkConfigs
   */
  saveZkConfigs: function (data) {
    var configs = {};
    var attributes = {};
    this.saveLoadedConfigs(data);
    data.items.forEach(function (item) {
      configs[item.type] = item.properties;
      attributes[item.type] = item.properties_attributes || {};
    }, this);

    this.updateZkConfigs(configs);
    var groups = [];
    var installedServiceNames = App.Service.find().mapProperty('serviceName');
    var zooKeeperRelatedServices = this.get('zooKeeperRelatedServices').slice(0);
    if (App.get('isHaEnabled')) {
      zooKeeperRelatedServices.push({
        serviceName: 'HDFS',
        typesToLoad: ['core-site'],
        typesToSave: ['core-site']
      });
    }
    zooKeeperRelatedServices.forEach(function (service) {
      if (installedServiceNames.contains(service.serviceName)) {
        var group = {
          properties: {},
          properties_attributes: {}
        };
        service.typesToSave.forEach(function (type) {
          group.properties[type] = configs[type];
          group.properties_attributes[type] = attributes[type];
        });
        groups.push(group);
      }
    });
    this.setConfigsChanges(groups);
  },

  /**
   * Is deleteHost action id fired
   * @type {bool}
   */
  fromDeleteHost: false,

  /**
   * Is ZooKeeper Server being deleted from host
   * @type {bool}
   */
  fromDeleteZkServer: false,

  /**
   * Send command to server to install selected host component
   * @param {Object} event
   * @method installComponent
   */
  installComponent: function (event) {
    var self = this;
    var component = event.context;
    var componentName = component.get('componentName');
    var displayName = component.get('displayName');

    return App.ModalPopup.show({
      primary: Em.I18n.t('hosts.host.installComponent.popup.confirm'),
      header: Em.I18n.t('popup.confirmation.commonHeader'),
      installComponentMessage: Em.I18n.t('hosts.host.installComponent.msg').format(displayName),
      bodyClass: Em.View.extend({
        templateName: require('templates/main/host/details/installComponentPopup')
      }),
      onPrimary: function () {
        var _this = this;
        App.get('router.mainAdminKerberosController').getSecurityType(function () {
          App.get('router.mainAdminKerberosController').getKDCSessionState(function () {
            _this.hide();

            App.ajax.send({
              name: 'common.host.host_component.update',
              sender: self,
              data: {
                hostName: self.get('content.hostName'),
                serviceName: component.get('service.serviceName'),
                componentName: componentName,
                component: component,
                context: Em.I18n.t('requestInfo.installHostComponent') + " " + displayName,
                HostRoles: {
                  state: 'INSTALLED'
                }
              },
              success: 'installComponentSuccessCallback',
              error: 'ajaxErrorCallback',
              showLoadingPopup: true
            });
          })
        });
      }
    });
  },

  /**
   * Success callback for install component request
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method installComponentSuccessCallback
   */
  installComponentSuccessCallback: function (data, opt, params) {
    if (App.get('testMode')) {
      this.mimicWorkStatusChange(params.component, App.HostComponentStatus.installing, App.HostComponentStatus.stopped);
    }
    this.showBackgroundOperationsPopup();
  },

  /**
   * Send command to server to run decommission on DATANODE, TASKTRACKER, NODEMANAGER, REGIONSERVER
   * @param {App.HostComponent} component
   * @param {callback} callback
   * @method decommission
   */
  decommission: function (component, callback) {
    var self = this;
    return App.showConfirmationPopup(function () {
      self.runDecommission.call(self, self.get('content.hostName'), component.get('service.serviceName'));
      if (callback) {
        callback()
      }
    }, Em.I18n.t('question.sure.decommission').format(component.get('service.serviceName')));
  },
  /**
   * identify correct component to run decommission on them by service name,
   * in result run proper decommission method
   * @param hostName
   * @param svcName
   */
  runDecommission: function (hostName, svcName) {
    switch (svcName) {
      case 'HDFS':
        this.doDecommission(hostName, svcName, "NAMENODE", "DATANODE");
        break;
      case 'YARN':
        this.doDecommission(hostName, svcName, "RESOURCEMANAGER", "NODEMANAGER");
        break;
      case 'HBASE':
        this.warnBeforeDecommission(hostName);
    }
  },

  /**
   * Send command to server to run recommission on DATANODE, TASKTRACKER, NODEMANAGER
   * @param {App.HostComponent} component
   * @param {callback} callback
   * @method recommission
   */
  recommission: function (component, callback) {
    var self = this;
    return App.showConfirmationPopup(function () {
      self.runRecommission.call(self, self.get('content.hostName'), component.get('service.serviceName'));
      if (callback) {
        callback()
      }
    }, Em.I18n.t('question.sure.recommission').format(component.get('service.serviceName')));
  },
  /**
   * identify correct component to run recommission on them by service name,
   * in result run proper recommission method
   * @param hostName
   * @param svcName
   */
  runRecommission: function (hostName, svcName) {
    switch (svcName) {
      case 'HDFS':
        this.doRecommissionAndStart(hostName, svcName, "NAMENODE", "DATANODE");
        break;
      case 'YARN':
        this.doRecommissionAndStart(hostName, svcName, "RESOURCEMANAGER", "NODEMANAGER");
        break;
      case 'HBASE':
        this.doRecommissionAndStart(hostName, svcName, "HBASE_MASTER", "HBASE_REGIONSERVER");
    }
    this.showBackgroundOperationsPopup();
  },

  /**
   * Performs Decommission (for DN, TT and NM)
   * @param {string} hostName
   * @param {string} serviceName
   * @param {string} componentName
   * @param {string} slaveType
   * @method doDecommission
   */
  doDecommission: function (hostName, serviceName, componentName, slaveType) {
    var contextNameString = 'hosts.host.' + slaveType.toLowerCase() + '.decommission';
    var context = Em.I18n.t(contextNameString);
    App.ajax.send({
      name: 'host.host_component.decommission_slave',
      sender: this,
      data: {
        context: context,
        command: 'DECOMMISSION',
        hostName: hostName,
        serviceName: serviceName,
        componentName: componentName,
        slaveType: slaveType
      },
      success: 'decommissionSuccessCallback',
      error: 'decommissionErrorCallback',
      showLoadingPopup: true
    });
  },

  /**
   * check is hbase regionserver in mm. If so - run decommission
   * otherwise shows warning
   * @method warnBeforeDecommission
   * @param {string} hostNames - list of host when run from bulk operations or current host
   */
  warnBeforeDecommission: function (hostNames) {
    if (this.get('content.hostComponents').findProperty('componentName', 'HBASE_REGIONSERVER').get('passiveState') == "OFF") {
      this.showHbaseActiveWarning();
    } else {
      this.checkRegionServerState(hostNames);
    }
  },

  /**
   *  send call to check is this regionserver last in cluster which has desired_admin_state property "INSERVICE"
   * @method checkRegionServerState
   * @param hostNames
   */
  checkRegionServerState: function (hostNames) {
    return App.ajax.send({
      name: 'host.region_servers.in_inservice',
      sender: this,
      data: {
        hostNames: hostNames
      },
      success: 'checkRegionServerStateSuccessCallback'
    });
  },

  /**
   * check is this regionserver last in cluster which has desired_admin_state property "INSERVICE"
   * @method checkRegionServerStateSuccessCallback
   * @param data
   * @param opt
   * @param params
   */
  checkRegionServerStateSuccessCallback: function (data, opt, params) {
    var hostArray = params.hostNames.split(",");
    var decommissionPossible = (data.items.mapProperty('HostRoles.host_name').filter(function (hostName) {
      return !hostArray.contains(hostName);
    }, this).length >= 1);
    if (decommissionPossible) {
      this.doDecommissionRegionServer(params.hostNames, "HBASE", "HBASE_MASTER", "HBASE_REGIONSERVER");
    } else {
      this.showRegionServerWarning();
    }
  },

  /**
   * show warning that regionserver is last in cluster which has desired_admin_state property "INSERVICE"
   * @method showRegionServerWarning
   * @param hostNames
   */
  showRegionServerWarning: function () {
    return App.ModalPopup.show({
      header: Em.I18n.t('common.warning'),
      message: Em.I18n.t('hosts.host.hbase_regionserver.decommission.warning'),
      bodyClass: Ember.View.extend({
        template: Em.Handlebars.compile('<div class="alert alert-warning">{{message}}</div>')
      }),
      secondary: false
    });
  },

  /**
   * shows warning: put hbase regionserver in passive state
   * @method showHbaseActiveWarning
   * @return {App.ModalPopup}
   */
  showHbaseActiveWarning: function () {
    return App.ModalPopup.show({
      header: Em.I18n.t('common.warning'),
      message: Em.I18n.t('hostPopup.recommendation.beforeDecommission').format(App.format.components["HBASE_REGIONSERVER"]),
      bodyClass: Ember.View.extend({
        template: Em.Handlebars.compile('<div class="alert alert-warning">{{message}}</div>')
      }),
      secondary: false
    });
  },

  /**
   * Performs Decommission (for RegionServer)
   * @method doDecommissionRegionServer
   * @param {string} hostNames - list of host when run from bulk operations or current host
   * @param {string} serviceName - serviceName
   * @param {string} componentName - master compoent name
   * @param {string} slaveType - slave component name
   */
  doDecommissionRegionServer: function (hostNames, serviceName, componentName, slaveType) {
    var batches = [
      {
        "order_id": 1,
        "type": "POST",
        "uri": "/clusters/" + App.get('clusterName') + "/requests",
        "RequestBodyInfo": {
          "RequestInfo": {
            "context": Em.I18n.t('hosts.host.regionserver.decommission.batch1'),
            "command": "DECOMMISSION",
            "exclusive": "true",
            "parameters": {
              "slave_type": slaveType,
              "excluded_hosts": hostNames
            },
            'operation_level': {
              level: "HOST_COMPONENT",
              cluster_name: App.get('clusterName'),
              host_name: hostNames,
              service_name: serviceName
            }
          },
          "Requests/resource_filters": [
            {"service_name": serviceName, "component_name": componentName}
          ]
        }
      }];
    var id = 2;
    var hAray = hostNames.split(",");
    for (var i = 0; i < hAray.length; i++) {
      batches.push({
        "order_id": id,
        "type": "PUT",
        "uri": "/clusters/" + App.get('clusterName') + "/hosts/" + hAray[i] + "/host_components/" + slaveType,
        "RequestBodyInfo": {
          "RequestInfo": {
            context: Em.I18n.t('hosts.host.regionserver.decommission.batch2'),
            exclusive: true,
            operation_level: {
              level: "HOST_COMPONENT",
              cluster_name: App.get('clusterName'),
              host_name: hostNames,
              service_name: serviceName || null
            }
          },
          "Body": {
            HostRoles: {
              state: "INSTALLED"
            }
          }
        }
      });
      id++;
    }
    batches.push({
      "order_id": id,
      "type": "POST",
      "uri": "/clusters/" + App.get('clusterName') + "/requests",
      "RequestBodyInfo": {
        "RequestInfo": {
          "context": Em.I18n.t('hosts.host.regionserver.decommission.batch3'),
          "command": "DECOMMISSION",
          "service_name": serviceName,
          "component_name": componentName,
          "parameters": {
            "slave_type": slaveType,
            "excluded_hosts": hostNames,
            "mark_draining_only": true
          },
          'operation_level': {
            level: "HOST_COMPONENT",
            cluster_name: App.get('clusterName'),
            host_name: hostNames,
            service_name: serviceName
          }
        },
        "Requests/resource_filters": [
          {"service_name": serviceName, "component_name": componentName}
        ]
      }
    });
    App.ajax.send({
      name: 'common.batch.request_schedules',
      sender: this,
      data: {
        intervalTimeSeconds: 1,
        tolerateSize: 0,
        batches: batches
      },
      success: 'decommissionSuccessCallback',
      error: 'decommissionErrorCallback',
      showLoadingPopup: true
    });
  },

  /**
   * Error callback for decommission requests
   * @param {object} request
   * @param {object} ajaxOptions
   * @param {string} error
   * @method decommissionErrorCallback
   */
  decommissionErrorCallback: function (request, ajaxOptions, error) {
  },

  /**
   * Success ajax response for Recommission/Decommission slaves
   * @param {object} data
   * @method decommissionSuccessCallback
   * @return {Boolean}
   */
  decommissionSuccessCallback: function (data) {
    if (data && (data.Requests || data.resources[0].RequestSchedule)) {
      this.showBackgroundOperationsPopup();
      return true;
    } else {
      return false;
    }
  },

  /**
   * Performs Recommission and Start
   * @param {string} hostNames
   * @param {string} serviceName
   * @param {string} componentName
   * @param {string} slaveType
   * @method doRecommissionAndStart
   */
  doRecommissionAndStart: function (hostNames, serviceName, componentName, slaveType) {
    var contextNameString_1 = 'hosts.host.' + slaveType.toLowerCase() + '.recommission';
    var context_1 = Em.I18n.t(contextNameString_1);
    var contextNameString_2 = 'requestInfo.startHostComponent.' + slaveType.toLowerCase();
    var startContext = Em.I18n.t(contextNameString_2);
    var params = {
      "slave_type": slaveType,
      "included_hosts": hostNames
    };
    if (serviceName == "HBASE") {
      params.mark_draining_only = true;
    }
    var batches = [
      {
        "order_id": 1,
        "type": "POST",
        "uri": "/clusters/" + App.get('clusterName') + "/requests",
        "RequestBodyInfo": {
          "RequestInfo": {
            "context": context_1,
            "command": "DECOMMISSION",
            "exclusive": "true",
            "parameters": params,
            'operation_level': {
              level: "HOST_COMPONENT",
              cluster_name: App.get('clusterName'),
              host_name: hostNames,
              service_name: serviceName
            }
          },
          "Requests/resource_filters": [
            {"service_name": serviceName, "component_name": componentName}
          ]
        }
      }];
    var id = 2;
    var hAray = hostNames.split(",");
    for (var i = 0; i < hAray.length; i++) {
      batches.push({
        "order_id": id,
        "type": "PUT",
        "uri": "/clusters/" + App.get('clusterName') + "/hosts/" + hAray[i] + "/host_components/" + slaveType,
        "RequestBodyInfo": {
          "RequestInfo": {
            context: startContext,
            operation_level: {
              level: "HOST_COMPONENT",
              cluster_name: App.get('clusterName'),
              host_name: hostNames,
              service_name: serviceName || null
            }
          },
          "Body": {
            HostRoles: {
              state: "STARTED"
            }
          }
        }
      });
      id++;
    }
    App.ajax.send({
      name: 'common.batch.request_schedules',
      sender: this,
      data: {
        intervalTimeSeconds: 1,
        tolerateSize: 1,
        batches: batches
      },
      success: 'decommissionSuccessCallback',
      error: 'decommissionErrorCallback',
      showLoadingPopup: true
    });
  },

  /**
   * Handler for host-menu items actions
   * @param {object} option
   * @method doAction
   */
  doAction: function (option) {
    switch (option.context.action) {
      case "deleteHost":
        this.validateAndDeleteHost();
        break;
      case "startAllComponents":
        if (!this.get('content.isNotHeartBeating')) this.doStartAllComponents();
        break;
      case "stopAllComponents":
        if (!this.get('content.isNotHeartBeating')) this.doStopAllComponents();
        break;
      case "restartAllComponents":
        if (!this.get('content.isNotHeartBeating')) this.doRestartAllComponents();
        break;
      case "onOffPassiveModeForHost":
        this.onOffPassiveModeForHost(option.context);
        break;
      case "setRackId":
        this.setRackIdForHost();
        break;
      case "checkHost":
        this.runHostCheckConfirmation();
        break;
      case "regenerateKeytabFileOperations":
        this.regenerateKeytabFileOperations();
        break;
    }
  },

  /**
   * Turn On/Off Passive Mode for host
   * @param {object} context
   * @method onOffPassiveModeForHost
   */
  onOffPassiveModeForHost: function (context) {
    var state = context.active ? 'ON' : 'OFF';
    var self = this;
    var message = Em.I18n.t('hosts.host.details.for.postfix').format(context.label);
    var popupInfo = Em.I18n.t('hosts.passiveMode.popup').format(context.active ? 'On' : 'Off', this.get('content.hostName'));
    if (state === 'OFF') {
      var currentHostVersion = this.get('content.stackVersions') && this.get('content.stackVersions').findProperty('isCurrent'),
        hostVersion = currentHostVersion && currentHostVersion.get('repoVersion'),
        currentVersion = App.StackVersion.find().findProperty('isCurrent'),
        clusterVersion = currentVersion && currentVersion.get('repositoryVersion.repositoryVersion');
      if (hostVersion !== clusterVersion) {
        var msg = Em.I18n.t("hosts.passiveMode.popup.version.mismatch").format(this.get('content.hostName'), clusterVersion);
        popupInfo += '<br/><div class="alert alert-warning">' + msg + '</div>';
      }
    }
    return App.showConfirmationPopup(function () {
        self.hostPassiveModeRequest(state, message);
      }, popupInfo);
  },

  /**
   * Set rack id for host
   * @method setRackIdForHost
   */
  setRackIdForHost: function () {
    var hostNames = [{hostName: this.get('content.hostName')}];
    var rack = this.get('content.rack');
    var operationData = {message: Em.I18n.t('hosts.host.details.setRackId')};
    hostsManagement.setRackInfo(operationData, hostNames, rack);
  },

  /**
   * Send request to get passive state for host
   * @param {string} state
   * @param {string} message
   * @method hostPassiveModeRequest
   */
  hostPassiveModeRequest: function (state, message) {
    App.ajax.send({
      name: 'bulk_request.hosts.passive_state',
      sender: this,
      dataType:'text',
      data: {
        hostNames: this.get('content.hostName'),
        passive_state: state,
        requestInfo: message
      },
      success: 'updateHost'
    });
  },

  /**
   * Success callback for receiving host passive state
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method updateHost
   */
  updateHost: function (data, opt, params) {
    this.set('content.passiveState', params.passive_state);
    batchUtils.infoPassiveState(params.passive_state);
  },

  /**
   * Send request to get passive state for hostComponent
   * @param {object} component - hostComponentn object
   * @param {string} state
   * @param {string} message
   * @method hostPassiveModeRequest
   */
  updateComponentPassiveState: function (component, state, message) {
    App.ajax.send({
      name: 'common.host.host_component.passive',
      sender: this,
      data: {
        hostName: this.get('content.hostName'),
        componentName: component.get('componentName'),
        component: component,
        passive_state: state,
        context: message
      },
      success: 'updateHostComponent'
    });
  },

  /**
   * Success callback for receiving hostComponent passive state
   * @param {object} data
   * @param {object} opt
   * @param {object} params
   * @method updateHost
   */
  updateHostComponent: function (data, opt, params) {
    params.component.set('passiveState', params.passive_state);
    batchUtils.infoPassiveState(params.passive_state);
  },

  /**
   * Show confirmation popup for action "start all components"
   * @method doStartAllComponents
   */
  doStartAllComponents: function () {
    var self = this;
    var components = this.get('serviceNonClientActiveComponents');

    if (components && components.get('length')) {
      return App.showConfirmationPopup(function () {
        self.sendComponentCommand(components, Em.I18n.t('hosts.host.maintainance.startAllComponents.context'), App.HostComponentStatus.started);
      }, Em.I18n.t('question.sure.startAll'));
    }
  },

  /**
   * Show confirmation popup for action "stop all components"
   * @method doStopAllComponents
   */
  doStopAllComponents: function () {
    var self = this;
    var components = this.get('serviceNonClientActiveComponents');

    if (components && components.get('length')) {
      if (components.someProperty('componentName', 'NAMENODE') &&
        this.get('content.hostComponents').filterProperty('componentName', 'NAMENODE').someProperty('workStatus', App.HostComponentStatus.started)) {
        this.checkNnLastCheckpointTime(function () {
          App.showConfirmationPopup(function () {
            self.sendComponentCommand(components, Em.I18n.t('hosts.host.maintainance.stopAllComponents.context'), App.HostComponentStatus.stopped);
          }, Em.I18n.t('question.sure.stopAll'));
        });
      } else {
        return App.showConfirmationPopup(function () {
          self.sendComponentCommand(components, Em.I18n.t('hosts.host.maintainance.stopAllComponents.context'), App.HostComponentStatus.stopped);
        }, Em.I18n.t('question.sure.stopAll'));
      }
    }
  },

  /**
   * Show confirmation popup for action "restart all components"
   * @method doRestartAllComponents
   */
  doRestartAllComponents: function () {
    var self = this;
    var components = this.get('serviceActiveComponents');

    if (components && components.get('length')) {
      if (components.someProperty('componentName', 'NAMENODE') &&
        this.get('content.hostComponents').filterProperty('componentName', 'NAMENODE').someProperty('workStatus', App.HostComponentStatus.started)) {
        this.checkNnLastCheckpointTime(function () {
          App.showConfirmationPopup(function () {
            batchUtils.restartHostComponents(components, Em.I18n.t('rollingrestart.context.allOnSelectedHost').format(self.get('content.hostName')), "HOST");
          }, Em.I18n.t('question.sure.restartAll'));
        });
      } else {
        return App.showConfirmationPopup(function () {
          batchUtils.restartHostComponents(components, Em.I18n.t('rollingrestart.context.allOnSelectedHost').format(self.get('content.hostName')), "HOST");
        }, Em.I18n.t('question.sure.restartAll'));
      }
    }
  },

  /**
   * get info about host-components, exactly:
   *  - host-components grouped by status, features
   *  - flag, that indicate whether ZooKeeper Server is installed
   * @return {Object}
   */
  getHostComponentsInfo: function (hostComponents) {
    const componentsOnHost = hostComponents || this.get('content.hostComponents');
    const stoppedStates = [App.HostComponentStatus.stopped,
      App.HostComponentStatus.install_failed,
      App.HostComponentStatus.upgrade_failed,
      App.HostComponentStatus.init,
      App.HostComponentStatus.unknown];
    const container = {
      isReconfigureRequired: false,
      lastComponents: [],
      masterComponents: [],
      nonAddableMasterComponents: [],
      lastMasterComponents: [],
      runningComponents: [],
      nonDeletableComponents: [],
      unknownComponents: [],
      toDecommissionComponents: []
    };
    const addDeleteComponentsMap = this.get('addDeleteComponentsMap');

    if (componentsOnHost && componentsOnHost.get('length') > 0) {
      componentsOnHost.forEach(function (cInstance) {
        if (addDeleteComponentsMap[cInstance.get('componentName')]) {
          container.isReconfigureRequired = true;
        }
        let isLastComponent = false;
        if (this.getTotalComponent(cInstance) === 1) {
          container.lastComponents.push(cInstance.get('displayName'));
          isLastComponent = true;
        }
        const workStatus = cInstance.get('workStatus');

        if (cInstance.get('isMaster')) {
          const displayName = cInstance.get('displayName');
          container.masterComponents.push(displayName);
          if (!App.StackServiceComponent.find(cInstance.get('componentName')).get('isMasterAddableInstallerWizard'))  {
            container.nonAddableMasterComponents.push(displayName);
          }
          if (isLastComponent) {
            container.lastMasterComponents.push(displayName);
          }
        }
        if (stoppedStates.indexOf(workStatus) < 0) {
          container.runningComponents.push(cInstance.get('displayName'));
        }
        if (!cInstance.get('isDeletable')) {
          container.nonDeletableComponents.push(cInstance.get('displayName'));
        }
        if (workStatus === App.HostComponentStatus.unknown) {
          container.unknownComponents.push(cInstance.get('displayName'));
        }
        if (App.get('components.decommissionAllowed').contains(cInstance.get('componentName')) && !cInstance.get('view.isComponentRecommissionAvailable')) {
          container.toDecommissionComponents.push(cInstance.get('displayName'));
        }
      }, this);
    }
    return container;
  },

  /**
   * Run host check confirmation
   * @method runHostCheckConfirmation
   */
  runHostCheckConfirmation: function () {
    var self = this;
    var popupInfo = Em.I18n.t('hosts.checkHost.popup').format(this.get('content.hostName'));

    return App.showConfirmationPopup(function () {
      self.runHostCheck();
    }, popupInfo);
  },

  getDataForHostCheck: function () {
    var hostName = this.get('content.hostName');
    var jdk_location = App.router.get('clusterController.ambariProperties.jdk_location');
    var RequestInfo = {
      "action": "check_host",
      "context": "Check host",
      "parameters": {
        "hosts" : hostName,
        "check_execute_list": "last_agent_env_check,installed_packages,existing_repos,transparentHugePage",
        "jdk_location" : jdk_location,
        "threshold": "20"
      }
    };

    return {
      RequestInfo: RequestInfo,
      resource_filters: {"hosts": hostName}
    };
  },

  /**
   * Callback for runHostCheckConfirmation
   * @method runHostCheck
   */
  runHostCheck: function () {
    var dataForCheckHostRequest = this.getDataForHostCheck();

    this.setProperties({
      stopChecking: false,
      checkHostFinished: false,
      isRerun: false
    });
    this.setBootHostsProp();
    this.showHostWarningsPopup();
    this.requestToPerformHostCheck(dataForCheckHostRequest);
  },

  /**
   * Shape controller's bootHosts property needed to host check
   * @method setBootHostsProp
   */
  setBootHostsProp: function () {
    var host = this.get('content');
    var bootHosts = [];

    host.name = host.get('hostName');
    bootHosts.push(host);

    this.set('bootHosts', bootHosts);
  },

  /**
   * Open popup that contain hosts' warnings
   * @return {App.ModalPopup}
   * @method showHostWarningsPopup
   */
  showHostWarningsPopup: function () {
    var self = this;

    return App.ModalPopup.show({

      header: Em.I18n.t('installer.step3.warnings.popup.header'),

      secondary: Em.I18n.t('installer.step3.hostWarningsPopup.rerunChecks'),

      primary: Em.I18n.t('common.close'),

      autoHeight: false,

      onPrimary: function () {
        self.set('checksUpdateStatus', null);
        this.hide();
      },

      onClose: function () {
        self.set('checksUpdateStatus', null);
        this.hide();
      },

      onSecondary: function () {
        self.set('checkHostFinished', false);
        self.rerunChecks();
      },

      didInsertElement: function () {
        this._super();
        this.fitHeight();
      },

      footerClass: App.WizardStep3HostWarningPopupFooter.reopen({
        footerControllerBinding: 'App.router.mainHostDetailsController',
        checkHostFinished: function () {
          return this.get('footerController.checkHostFinished');
        }.property('footerController.checkHostFinished')
      }),

      bodyClass: App.WizardStep3HostWarningPopupBody.reopen({
        bodyControllerBinding: 'App.router.mainHostDetailsController',
        checkHostFinished: function () {
          return this.get('bodyController.checkHostFinished');
        }.property('bodyController.checkHostFinished')
      })
    });
  },


  /**
   * Deletion of hosts not supported for this version
   * @method validateAndDeleteHost
   */
  validateAndDeleteHost: function () {
    const container = this.getHostComponentsInfo();

    if (container.nonDeletableComponents.length > 0) {
      this.raiseDeleteComponentsError(container, 'nonDeletableList');
      return;
    } else if (container.nonAddableMasterComponents.length > 0) {
      this.raiseDeleteComponentsError(container, 'masterList');
      return;
    } else if (container.runningComponents.length > 0) {
      this.raiseDeleteComponentsError(container, 'runningList');
      return;
    } else if(container.lastMasterComponents.length > 0) {
      this.raiseDeleteComponentsError(container, 'lastMasterList');
      return;
    }

    this.set('fromDeleteHost', true);

    if (container.isReconfigureRequired) {
      this.reconfigureAndDeleteHost(container);
    } else {
      this.confirmDeleteHost(container);
    }
  },

  /**
   *
   * @param {object} container
   */
  reconfigureAndDeleteHost: function(container) {
    const addDeleteComponentsMap = this.get('addDeleteComponentsMap');
    const hostName = this.get('content.hostName');
    const reconfiguredComponents = [];
    const componentStub = Em.Object.create();

    this.get('content.hostComponents').forEach((component) => {
      const componentsMapItem = addDeleteComponentsMap[component.get('componentName')];
      if (componentsMapItem) {
        reconfiguredComponents.push(component.get('displayName'));
        if (componentsMapItem.hostPropertyName) {
          this.set(componentsMapItem.hostPropertyName, hostName);
        }
        if (componentsMapItem.addPropertyName) {
          this.set(componentsMapItem.addPropertyName, true);
        }
        this.loadComponentRelatedConfigs(componentsMapItem.configTagsCallbackName, componentsMapItem.configsCallbackName);
      }
    });
    this.showReconfigurationPopupPreDelete(componentStub, () => {
      this.confirmDeleteHost(container);
    }, Em.I18n.t('hosts.host.delete.componentsRequireReconfigure').format(reconfiguredComponents.join(', ')));
  },

  /**
   * Show popup with info about reasons why host can't be deleted
   * @param {string[]} components
   * @param {string} type
   * @method raiseDeleteComponentsError
   */
  raiseDeleteComponentsError: function (container, type) {
    App.ModalPopup.show({
      header: Em.I18n.t('hosts.cant.do.popup.title'),
      type: type,
      showBodyEnd: Em.computed.existsIn('type', ['runningList', 'masterList', 'lastMasterList']),
      container: container,
      components: function(){
        var container = this.get('container');
        switch (this.get('type')) {
          case 'masterList':
            return container.nonAddableMasterComponents;
          case 'nonDeletableList':
            return container.nonDeletableComponents;
          case 'runningList':
            return container.runningComponents;
          case 'lastMasterList':
            return container.lastMasterComponents;
        }
      }.property('type'),
      componentsStr: function () {
        return this.get('components').join(", ");
      }.property('components.[]'),
      componentsBody: Em.computed.i18nFormat('hosts.cant.do.popup.' + type + '.body', 'components.length'),
      componentsBodyEnd: function () {
        if (this.get('showBodyEnd')) {
          return Em.I18n.t('hosts.cant.do.popup.' + type + '.body.end').format(App.get('components.decommissionAllowed').map(function(c){return App.format.role(c, false)}).join(", "));
        }
        return '';
      }.property(),
      bodyClass: Em.View.extend({
        templateName: require('templates/main/host/details/raiseDeleteComponentErrorPopup')
      }),
      secondary: null
    });
  },

  /**
   * Show confirmation popup to delete host
   * @param {Object} container
   * @method confirmDeleteHost
   */
  confirmDeleteHost: function (container) {
    var self = this;
    return App.ModalPopup.show({
      header: Em.I18n.t('hosts.delete.popup.title'),
      deletePopupBody: Em.I18n.t('hosts.delete.popup.body').format(self.get('content.publicHostName')),
      lastComponent: function () {
        if (container.lastComponents && container.lastComponents.length) {
          this.set('isChecked', false);
          return true;
        } else {
          this.set('isChecked', true);
          return false;
        }
      }.property(),
      disablePrimary: Em.computed.not('isChecked'),
      isChecked: false,
      lastComponentError: Em.View.extend({
        template: Em.Handlebars.compile(Em.I18n.t('hosts.delete.popup.body.msg4').format(container.lastComponents))
      }),
      unknownComponents: function () {
        if (container.unknownComponents && container.unknownComponents.length) {
          return container.unknownComponents.join(", ");
        }
        return '';
      }.property(),
      decommissionWarning: Em.View.extend({
        template: Em.Handlebars.compile(Em.I18n.t('hosts.delete.popup.body.msg7').format(container.toDecommissionComponents.join(', ')))
      }),
      toDecommissionComponents: container.toDecommissionComponents,
      bodyClass: Em.View.extend({
        templateName: require('templates/main/host/details/doDeleteHostPopup')
      }),
      onPrimary: function () {
        this.hide();
        self.doDeleteHost();
      }
    });
  },

  /**
   * send DELETE calls to components of host and after delete host itself
   * @method doDeleteHost
   */
  doDeleteHost: function () {
    const allComponents = this.get('content.hostComponents');
    const addDeleteComponentsMap = this.get('addDeleteComponentsMap');
    const deleteRequests = [];
    let deleteError = null;
    const dfd = $.Deferred();

    if (allComponents.get('length') > 0) {
      allComponents.forEach(function (component) {
        deleteRequests.push(this._doDeleteHostComponent(component.get('componentName')));
      }, this);
      $.when(deleteRequests).done(() => {
        if (this.get('isReconfigureRequired')) {
          const reconfiguredComponents = allComponents
            .filter((component) => addDeleteComponentsMap[component.get('componentName')])
            .mapProperty('displayName').join(', ');
          this.applyConfigsCustomization();
          this.putConfigsToServer(this.get('groupedPropertiesToChange'), reconfiguredComponents);
          this.clearConfigsChanges();
        }
        this.deleteHostCall();
      }).fail(() => {
        deleteError = this.get('_deletedHostComponentError');
        deleteError.xhr.responseText = "{\"message\": \"" + deleteError.xhr.statusText + "\"}";
        App.ajax.defaultErrorHandler(deleteError.xhr, deleteError.url, deleteError.type, deleteError.xhr.status);
      }).always(dfd.resolve);
    } else {
      dfd.resolve();
    }
    return dfd.promise();
  },

  deleteHostCall: function() {
    return App.ajax.send({
      name: 'common.delete.host',
      sender: this,
      data: {
        hostName: this.get('content.hostName')
      },
      success: 'deleteHostCallSuccessCallback',
      error: 'deleteHostCallErrorCallback',
      showLoadingPopup: true
    });
  },

  deleteHostCallSuccessCallback: function (data, rq, requestBody) {
    App.router.get('updateController').updateHost(function () {
      App.router.transitionTo('hosts.index');
    });
    if (!!(requestBody && requestBody.hostName)) {
      var remainingHosts = App.db.getSelectedHosts().removeObject(requestBody.hostName);
      App.db.setSelectedHosts(remainingHosts);
      App.hostsMapper.deleteRecord(App.Host.find().findProperty('hostName', requestBody.hostName));
    }
    App.router.get('clusterController').getAllHostNames();
  },
  deleteHostCallErrorCallback: function (xhr, textStatus, errorThrown, opt) {
    xhr.responseText = "{\"message\": \"" + xhr.statusText + "\"}";
    App.ajax.defaultErrorHandler(xhr, opt.url, 'DELETE', xhr.status);
  },

  /**
   * Send command to server to restart all host components with stale configs
   * @method restartAllStaleConfigComponents
   */
  restartAllStaleConfigComponents: function () {
    var self = this;
    var staleComponents = self.get('content.componentsWithStaleConfigs');
    if (staleComponents.someProperty('componentName', 'NAMENODE') &&
      this.get('content.hostComponents').filterProperty('componentName', 'NAMENODE').someProperty('workStatus', App.HostComponentStatus.started)) {
      this.checkNnLastCheckpointTime(function () {
        App.showConfirmationPopup(function () {
          batchUtils.restartHostComponents(staleComponents, Em.I18n.t('rollingrestart.context.allWithStaleConfigsOnSelectedHost').format(self.get('content.hostName')), "HOST");
        });
      });
    } else {
      return App.showConfirmationPopup(function () {
        batchUtils.restartHostComponents(staleComponents, Em.I18n.t('rollingrestart.context.allWithStaleConfigsOnSelectedHost').format(self.get('content.hostName')), "HOST");
      });
    }

  },

  /**
   * open Reassign Master Wizard with selected component
   * @param {object} event
   * @method moveComponent
   */
  moveComponent: function (event) {
    var component = event.context;
    if ($(event.target).closest('li').hasClass('disabled')) {
      return;
    }
    return App.showConfirmationPopup(function () {
      var reassignMasterController = App.router.get('reassignMasterController');
      reassignMasterController.saveComponentToReassign(component);
      reassignMasterController.setCurrentStep('1');
      App.router.transitionTo('reassign');
    }, Em.I18n.t('question.sure.move').format(component.get('displayName')));
  },

  /**
   * Restart clients host components to apply config changes
   * @param {object} event
   * @method refreshConfigs
   */
  refreshConfigs: function (event) {
    var self = this;
    var component = event.context;
    if (!Em.isNone(component)) {
      return App.showConfirmationPopup(function () {
        var message = Em.I18n.t('rollingrestart.context.ClientOnSelectedHost')
            .format(component.get('displayName'), self.get('content.hostName'));
        batchUtils.restartHostComponents([component], message, "HOST");
      }, Em.I18n.t('question.sure.refresh').format(component.get('displayName'), self.get('content.hostName')));
    }
  },

  toggleMaintenanceMode: function (event) {
    var state, message, self = this;
    if (event.context.get('isImpliedState')) return null;
    state = event.context.get('passiveState') === "ON" ? "OFF" : "ON";
    message = Em.I18n.t('passiveState.turn' + state.toCapital() + 'For').format(event.context.get('displayName'));
    return App.showConfirmationPopup(function () {
      self.updateComponentPassiveState(event.context, state, message);
    }, Em.I18n.t('question.sure.maintenance').format(state.toLowerCase(), event.context.get('displayName')) );
  },

  downloadClientConfigs: function (event) {
    this.downloadClientConfigsCall({
      hostName: event.context.get('hostName'),
      componentName: event.context.get('componentName'),
      resourceType: this.resourceTypeEnum.HOST_COMPONENT
    });
  },

  /**
   *  This controller action is called from the template when user clicks to download configs for "All Clients On Host"
   */
  downloadAllClientConfigs: function () {
    var self = this;
    this.downloadClientConfigsCall({
      hostName: self.get('content.hostName'),
      resourceType: this.resourceTypeEnum.HOST
    });
  },

  installClients: function (components) {
    var clientsToInstall = [],
      clientsToAdd = [],
      missedComponents = [],
      dependentComponents = [],
      self = this;
    components.forEach(function (component) {
      if (['INIT', 'INSTALL_FAILED'].contains(component.get('workStatus'))) {
        clientsToInstall.push(component);
      } else if (typeof component.get('workStatus') == 'undefined') {
        clientsToAdd.push(component);
      }
    });
    clientsToAdd.forEach(function (component, index, array) {
      var dependencies = this.checkComponentDependencies(component.get('componentName'), {
        scope: 'host',
        installedComponents: this.get('content.hostComponents').mapProperty('componentName')
      }).reject(function (componentName) {
        return array.mapProperty('componentName').contains(componentName);
      });
      if (dependencies.length) {
        missedComponents.pushObjects(dependencies);
        dependentComponents.push(component.get('displayName'));
      }
    }, this);
    missedComponents = missedComponents.uniq();
    if (missedComponents.length) {
      var popupMessage = Em.I18n.t('host.host.addComponent.popup.clients.dependedComponents.body').format(stringUtils.getFormattedStringFromArray(dependentComponents),
        stringUtils.getFormattedStringFromArray(missedComponents.map(function (componentName) {
          return App.StackServiceComponent.find(componentName).get('displayName');
        })));
      App.showAlertPopup(Em.I18n.t('host.host.addComponent.popup.dependedComponents.header'), popupMessage);
    } else {
      App.get('router.mainAdminKerberosController').getSecurityType(function () {
        App.get('router.mainAdminKerberosController').getKDCSessionState(function () {
          var sendInstallCommand = function () {
            if (clientsToInstall.length) {
              self.sendComponentCommand(clientsToInstall, Em.I18n.t('host.host.details.installClients'), 'INSTALLED');
            }
          };
          if (clientsToAdd.length) {
            var message = stringUtils.getFormattedStringFromArray(clientsToAdd.mapProperty('displayName')),
              componentObject = Em.Object.create({
                displayName: message
              });
            self.showAddComponentPopup(componentObject, self.get('content.hostName'), function () {
              sendInstallCommand();
              clientsToAdd.forEach(function (component) {
                self.installHostComponentCall(self.get('content.hostName'), component);
              });
            });
          } else {
            sendInstallCommand();
          }
        });
      }.bind(this));
    }
  },

  /**
   * 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;
    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;
    }
    var component = App.StackServiceComponent.find(componentName);
    const excludeExclusiveDependencies = (d) => d.type !== 'exclusive';
    return component.missingDependencies(installedComponents, opt)
    .filter(excludeExclusiveDependencies) //If type is "exclusive" the dependent component should never be co-hosted.
    .map(function(componentDependency) {
      return componentDependency.chooseCompatible();
    });
  },

  /**
   * On click handler for custom command from items menu
   * @param context
   */
  executeCustomCommand: function(event) {
    var controller = this;
    var context = event.context;
    return App.showConfirmationPopup(function() {
      App.ajax.send({
        name : 'service.item.executeCustomCommand',
        sender: controller,
        data : {
          command : context.command,
          context : context.context || Em.I18n.t('services.service.actions.run.executeCustomCommand.context').format(context.command),
          hosts : context.hosts,
          serviceName : context.service,
          componentName : context.component
        },
        success : 'executeCustomCommandSuccessCallback',
        error : 'executeCustomCommandErrorCallback'
      });
    });
  },

  executeCustomCommandSuccessCallback  : function(data, ajaxOptions, params) {
    if (data.Requests.id) {
      App.router.get('userSettingsController').dataLoading('show_bg').done(function (initValue) {
        if (initValue) {
          App.router.get('backgroundOperationsController').showPopup();
        }
      });
    }
  },
  executeCustomCommandErrorCallback : function(data) {
    var error = Em.I18n.t('services.service.actions.run.executeCustomCommand.error');
    if(data && data.responseText){
      try {
        var json = JSON.parse(data.responseText);
        error += json.message;
      } catch (err) {}
    }
    App.showAlertPopup(Em.I18n.t('services.service.actions.run.executeCustomCommand.error'), error);
  },

  /**
   * Call callback after loading service metrics
   * @param callback
   */
  isServiceMetricsLoaded: function(callback) {
    App.router.get('mainController').isLoading.call(App.router.get('clusterController'), 'isServiceContentFullyLoaded').done(callback);
  },

  setConfigsChangesForDisplay: function () {
    if (App.get('router.clusterController.isConfigsPropertiesLoaded')) {
      this.get('allPropertiesToChange').forEach(function (property) {
        var stackProperty = App.configsCollection.getConfigByName(property.propertyName, property.propertyFileName);
        if (stackProperty && (!stackProperty.isEditable || !stackProperty.isReconfigurable)) {
          this.get('requiredPropertiesToChange').pushObject(property);
        } else {
          Em.set(property, 'saveRecommended', true);
          this.get('recommendedPropertiesToChange').pushObject(property);
        }
      }, this);
      this.set('isConfigsLoadingInProgress', false);
      this.removeObserver('App.router.clusterController.isConfigsPropertiesLoaded', this, 'setConfigsChangesForDisplay');
    }
  },

  setConfigsChanges: function (groups) {
    this.get('groupedPropertiesToChange').pushObjects(groups);
    if (this.get('allPropertiesToChange.length')) {
      if (App.get('router.clusterController.isConfigsPropertiesLoaded')) {
        this.setConfigsChangesForDisplay();
      } else {
        this.addObserver('App.router.clusterController.isConfigsPropertiesLoaded', this, 'setConfigsChangesForDisplay');
      }
    } else {
      this.set('isConfigsLoadingInProgress', false);
    }
  },
  
  recoverHost: function() {
    var components = this.get('content.hostComponents');
    var hostName = this.get('content.hostName');
    var self = this;
    var batches = [
      {
        "order_id": 1,
        "type": "PUT",
        "uri": "/clusters/" + App.get('clusterName') + "/hosts/" + hostName + "/host_components",
        "RequestBodyInfo": {
          "RequestInfo": {
            context: Em.I18n.t('hosts.host.recover.initAllComponents.context'),
            operation_level: {
              level: "HOST",
              cluster_name: App.get('clusterName'),
              host_name: hostName
            },
            query: "HostRoles/component_name.in(" + components.mapProperty('componentName').join(',') + ")"
          },
          "Body": {
            HostRoles: {
              state: "INIT"
            }
          }
        }
    }];
    batches.push(
      {
        "order_id": 2,
        "type": "PUT",
        "uri": "/clusters/" + App.get('clusterName') + "/hosts/" + hostName + "/host_components",
        "RequestBodyInfo": {
          "RequestInfo": {
            context: Em.I18n.t('hosts.host.recover.installAllComponents.context'),
            operation_level: {
              level: "HOST",
              cluster_name: App.get('clusterName'),
              host_name: hostName
            },
            query: "HostRoles/component_name.in(" + components.mapProperty('componentName').join(',') + ")"
          },
          "Body": {
            HostRoles: {
              state: "INSTALLED"
            }
          }
        }
    });

    if(App.get('isKerberosEnabled')) {
      batches.push({
        "order_id": 3,
        "type": "PUT",
        "uri": "/clusters/" + App.get('clusterName'),
        "RequestBodyInfo": {
          "RequestInfo": {
            context: Em.I18n.t('hosts.host.recover.regenerateKeytabs.context'),
            query: "regenerate_keytabs=all&regenerate_hosts=" + hostName + "&config_update_policy=none",
          },
          "Body": {
            Clusters: {
             security_type: "KERBEROS"
            }
          }
        }
      });
    }
    App.get('router.mainAdminKerberosController').getSecurityType(function () {
      App.get('router.mainAdminKerberosController').getKDCSessionState(function () {
        self._doRecoverHost(batches);
      });
    });
  },

  _doRecoverHost: function (batches) {
    App.ajax.send ({
      name: 'common.batch.request_schedules',
      sender: this,
      data: {
        intervalTimeSeconds: 1,
        tolerateSize: 0,
        batches: batches
      },
      success:'recoverHostSuccessCallback',
      showLoadingPopup: true
    });
  },

  recoverHostSuccessCallback: function (data) {
    if (data && (data.Requests || data.resources[0].RequestSchedule)) {
      this.showBackgroundOperationsPopup();
      return true;
    } else {
      return false;
    }
  },

  recoverHostDisabled: function() {

    var isDisabled = false;
    var allowedStates = [App.HostComponentStatus.stopped, App.HostComponentStatus.install_failed, App.HostComponentStatus.init];
    this.get('content.hostComponents').forEach(function (component) {
      isDisabled = isDisabled ? true : !allowedStates.contains(component.get('workStatus'));
    });
    return isDisabled;
  }.property('content.hostComponents.@each.workStatus'),

  confirmRecoverHost: function() {
    var self = this;
    var componentsNotStopped = [];
    var allowedStates = [App.HostComponentStatus.stopped, App.HostComponentStatus.install_failed, App.HostComponentStatus.init];
    this.get('content.hostComponents').forEach(function (component) {
      if(!allowedStates.contains(component.get('workStatus'))) {
        componentsNotStopped.push(component.get('componentName'));
      }
    });
    if(componentsNotStopped.length) {
      App.ModalPopup.show({
        header: Em.I18n.t('hosts.recover.error.popup.title'),
        recoverErrorPopupBody: Em.I18n.t('hosts.recover.error.popup.body').format(componentsNotStopped.toString()),
        componentsStr: componentsNotStopped.toString(),
        bodyClass: Em.View.extend({
          templateName: require('templates/main/host/details/recoverHostErrorPopup')
        }),
        secondary: false
      });
    } else {
      App.ModalPopup.show({
        header: Em.I18n.t('hosts.recover.popup.title'),
        bodyClass: Em.View.extend({
          templateName: require('templates/main/host/details/recoverHostPopup')
        }),
        primary: Em.I18n.t('yes'),
        secondary: Em.I18n.t('no'),
        onPrimary: function () {
          self.recoverHost();
          this.hide();
        }
      });
    }
  },


  regenerateKeytabFileOperations: function () {
    var self = this;
    var hostName = this.content.get('hostName');
    var clusterName = App.get('clusterName');
    return App.showConfirmationPopup(function() {
      return App.ajax.send({
        name: "admin.kerberos_security.regenerate_keytabs.host",
        sender: self,
        data: {
          clusterName: clusterName,
          hostName: hostName
        },
        success: 'regenerateKeytabFileOperationsRequestSuccess',
        error: 'regenerateKeytabFileOperationsRequestError'
      });
    }, Em.I18n.t('question.sure.regenerateKeytab.host').format(hostName));
  },

  regenerateKeytabFileOperationsRequestSuccess: function(){
    App.router.get('backgroundOperationsController').showPopup();
  },

  regenerateKeytabFileOperationsRequestError: function () {
    App.showAlertPopup(Em.I18n.t('common.error'), Em.I18n.t('alerts.notifications.regenerateKeytab.host.error').format(this.content.get('hostName')));
  },

  /**
   * Returns URL parameters for configs request by certain tags
   * @param {object} data - object with desired configs tags received from API
   * @param {string[]} configTypes - list of config types
   * @returns {string}
   */
  getUrlParamsForConfigsRequest: function (data, configTypes) {
    return configTypes.map(type => {
      const tag = Em.get(data, `Clusters.desired_configs.${type}.tag`);
      return tag ? `(type=${type}&tag=${tag})` : null;
    }).compact().join('|');
  }

});
