You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jl...@apache.org on 2016/06/27 23:36:49 UTC

[23/34] ambari git commit: AMBARI-17355 & AMBARI-17354: POC: FE & BE changes for first class support for Yarn hosted services

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/controllers/main/service.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service.js b/ambari-web/app/controllers/main/service.js
index 44b8b21..e64518b 100644
--- a/ambari-web/app/controllers/main/service.js
+++ b/ambari-web/app/controllers/main/service.js
@@ -17,7 +17,6 @@
  */
 
 var App = require('app');
-var misc = require('utils/misc');
 
 App.MainServiceController = Em.ArrayController.extend({
 
@@ -30,9 +29,14 @@ App.MainServiceController = Em.ArrayController.extend({
     if (!App.router.get('clusterController.isLoaded')) {
       return [];
     }
-    return misc.sortByOrder(App.StackService.find().mapProperty('serviceName'), App.Service.find().toArray());
+    return App.ServiceGroup.find();
   }.property('App.router.clusterController.isLoaded').volatile(),
 
+  isAllCoreServicesInstalled: function() {
+    var coreServiceGroup = this.get('content').findProperty('serviceGroupName', 'CORE');
+    return coreServiceGroup ? this.get('content').findProperty('id', 'CORE').get('isAllServicesInstalled') : false;
+  }.property('content.@each.isAllServicesInstalled'),
+
   /**
    * Current cluster
    * @type {Ember.Object}
@@ -45,59 +49,15 @@ App.MainServiceController = Em.ArrayController.extend({
   }.property('App.router.clusterController.isClusterDataLoaded'),
 
   /**
-   * Check if all services are installed
-   * true - all installed, false - not all
-   * @type {bool}
-   */
-  isAllServicesInstalled: function () {
-    if (!this.get('content')) return false;
-    var availableServices = App.StackService.find().mapProperty('serviceName');
-    return this.get('content').length == availableServices.length;
-  }.property('content.@each', 'content.length'),
-
-  /**
-   * Should "Start All"-button be disabled
-   * @type {bool}
-   */
-  isStartAllDisabled: function () {
-    if (this.get('isStartStopAllClicked') == true) {
-      return true;
-    }
-    var stoppedServices = this.get('content').filter(function (_service) {
-      return (_service.get('healthStatus') === 'red' && !App.get('services.clientOnly').contains(_service.get('serviceName')));
-    });
-    return (stoppedServices.length === 0); // all green status
-  }.property('isStartStopAllClicked', 'content.@each.healthStatus'),
-
-  /**
-   * Should "Stop All"-button be disabled
-   * @type {bool}
-   */
-  isStopAllDisabled: function () {
-    if (this.get('isStartStopAllClicked') == true) {
-      return true;
-    }
-    return !this.get('content').someProperty('healthStatus', 'green');
-  }.property('isStartStopAllClicked', 'content.@each.healthStatus'),
-
-  /**
-   * Should "Refresh All"-button be disabled
-   * @type {bool}
-   */
-  isRestartAllRequiredDisabled: Em.computed.everyBy('content', 'isRestartRequired', false),
-
-  /**
-   * @type {bool}
-   */
-  isStartStopAllClicked: Em.computed.notEqual('App.router.backgroundOperationsController.allOperationsCount', 0),
-
-  /**
    * Callback for <code>start all service</code> button
    * @return {App.ModalPopup|null}
    * @method startAllService
    */
   startAllService: function (event) {
-    return this.startStopAllService(event, 'STARTED');
+    var serviceGroup = event.context;
+    if (!serviceGroup || !serviceGroup.get('isStartAllDisabled')) {
+      return this.startStopAllService(serviceGroup, 'STARTED');
+    }
   },
 
   /**
@@ -106,59 +66,81 @@ App.MainServiceController = Em.ArrayController.extend({
    * @method stopAllService
    */
   stopAllService: function (event) {
-    return this.startStopAllService(event, 'INSTALLED');
+    var serviceGroup = event.context;
+    if (!serviceGroup || !serviceGroup.get('isStopAllDisabled')) {
+      return this.startStopAllService(serviceGroup, 'INSTALLED');
+    }
   },
 
   /**
    * Common method for "start-all", "stop-all" calls
-   * @param {object} event
+   * @param {object} serviceGroup
    * @param {string} state 'STARTED|INSTALLED'
    * @returns {App.ModalPopup|null}
    * @method startStopAllService
    */
-  startStopAllService: function(event, state) {
-    if ($(event.target).hasClass('disabled') || $(event.target.parentElement).hasClass('disabled')) {
-      return null;
-    }
+  startStopAllService: function(serviceGroup, state) {
     var self = this;
+    var _serviceGroup = serviceGroup || Em.Object.create();
+    var serviceGroupName = _serviceGroup.get('serviceGroupName');
+    var isCoreServiceGroup = serviceGroupName === 'CORE';
+    var confirmStopMsg, confirmStartMsg;
+    if (serviceGroupName === 'CORE') {
+      confirmStopMsg = Em.I18n.t('services.service.core.stopAll.confirmMsg');
+      confirmStartMsg = Em.I18n.t('services.service.core.startAll.confirmMsg');
+    } else {
+      if(serviceGroupName) {
+        confirmStopMsg = Em.I18n.t('services.service.stopAll.confirmMsg');
+        confirmStartMsg = Em.I18n.t('services.service.startAll.confirmMsg');
+      }
+      else {
+        confirmStopMsg = Em.I18n.t('services.service.cluster.stopAll.confirmMsg');
+        confirmStartMsg = Em.I18n.t('services.service.cluster.startAll.confirmMsg');
+      }
+    }
     var bodyMessage = Em.Object.create({
-      confirmMsg: state == 'INSTALLED' ? Em.I18n.t('services.service.stopAll.confirmMsg') : Em.I18n.t('services.service.startAll.confirmMsg'),
-      confirmButton: state == 'INSTALLED' ? Em.I18n.t('services.service.stop.confirmButton') : Em.I18n.t('services.service.start.confirmButton')
+      confirmMsg: state === 'INSTALLED' ? confirmStopMsg.format(serviceGroupName) : confirmStartMsg.format(serviceGroupName),
+      confirmButton: state === 'INSTALLED' ? Em.I18n.t('services.service.stop.confirmButton') : Em.I18n.t('services.service.start.confirmButton')
     });
 
-    if (state == 'INSTALLED' && App.Service.find().filterProperty('serviceName', 'HDFS').someProperty('workStatus', App.HostComponentStatus.started)) {
-      App.router.get('mainServiceItemController').checkNnLastCheckpointTime(function () {
+    if (isCoreServiceGroup && state === 'INSTALLED' && App.Service.find().filterProperty('serviceName', 'HDFS').someProperty('workStatus', App.HostComponentStatus.started)) {
+      return App.router.get('mainServiceItemController').checkNnLastCheckpointTime(function () {
         return App.showConfirmationFeedBackPopup(function (query) {
-          self.allServicesCall(state, query);
+          self.allServicesCall(state, query, _serviceGroup);
         }, bodyMessage);
       });
-    } else {
-      return App.showConfirmationFeedBackPopup(function (query) {
-        self.allServicesCall(state, query);
-      }, bodyMessage);
     }
+    return App.showConfirmationFeedBackPopup(function (query) {
+      self.allServicesCall(state, query, _serviceGroup);
+    }, bodyMessage);
   },
 
   /**
    * Do request to server for "start|stop" all services
    * @param {string} state "STARTED|INSTALLED"
    * @param {object} query
+   * @param {object} serviceGroup
    * @method allServicesCall
    * @return {$.ajax}
    */
-  allServicesCall: function (state, query) {
-    var context = (state == 'INSTALLED') ? App.BackgroundOperationsController.CommandContexts.STOP_ALL_SERVICES :
+  allServicesCall: function (state, query, serviceGroup) {
+    var context = state === 'INSTALLED' ? App.BackgroundOperationsController.CommandContexts.STOP_ALL_SERVICES :
       App.BackgroundOperationsController.CommandContexts.START_ALL_SERVICES;
+    var services = serviceGroup.get('services') || App.Service.find();
+    var servicesList = services.mapProperty("serviceName").join(',');
+    var data = {
+      context: context,
+        ServiceInfo: {
+        state: state
+      },
+      urlParams: "ServiceInfo/service_name.in(" + servicesList + ")",
+      query: query
+    };
+
     return App.ajax.send({
       name: 'common.services.update',
       sender: this,
-      data: {
-        context: context,
-        ServiceInfo: {
-          state: state
-        },
-        query: query
-      },
+      data: data,
       success: 'allServicesCallSuccessCallback',
       error: 'allServicesCallErrorCallback'
     });
@@ -288,7 +270,7 @@ App.MainServiceController = Em.ArrayController.extend({
    * @method gotoAddService
    */
   gotoAddService: function () {
-    if (this.get('isAllServicesInstalled')) {
+    if (this.get('isAllCoreServicesInstalled')) {
       return;
     }
     App.router.get('addServiceController').setDBProperty('onClosePath', 'main.services.index');
@@ -298,9 +280,9 @@ App.MainServiceController = Em.ArrayController.extend({
   /**
    * Show confirmation popup and send request to restart all host components with stale_configs=true
    */
-  restartAllRequired: function () {
+  restartAllRequired: function (serviceGroup) {
     var self = this;
-    if (!this.get('isRestartAllRequiredDisabled')) {
+    if (!serviceGroup.get('isRestartAllRequiredDisabled')) {
       return App.showConfirmationPopup(function () {
             self.restartHostComponents();
           }, Em.I18n.t('services.service.refreshAll.confirmMsg').format(

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/controllers/main/service/info/configs.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/info/configs.js b/ambari-web/app/controllers/main/service/info/configs.js
index b6e6c21..57c013e 100644
--- a/ambari-web/app/controllers/main/service/info/configs.js
+++ b/ambari-web/app/controllers/main/service/info/configs.js
@@ -97,8 +97,8 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ConfigsLoader, A
    * @type {string[]}
    */
   servicesToLoad: function() {
-    return [this.get('content.serviceName')].concat(this.get('dependentServiceNames')).uniq();
-  }.property('content.serviceName', 'dependentServiceNames.length'),
+    return [this.get('content.stackServiceName')].concat(this.get('dependentServiceNames')).uniq();
+  }.property('content.stackServiceName', 'dependentServiceNames.length'),
 
   /**
    * @type {boolean}
@@ -291,7 +291,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ConfigsLoader, A
    * @method loadStep
    */
   loadStep: function () {
-    var serviceName = this.get('content.serviceName');
+    var serviceName = this.get('content.stackServiceName');
     this.clearStep();
     this.set('dependentServiceNames', App.StackService.find(serviceName).get('dependentServiceNames'));
     this.loadConfigTheme(serviceName).always(this.loadCurrentVersions.bind(this));

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/controllers/main/service/info/summary.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/info/summary.js b/ambari-web/app/controllers/main/service/info/summary.js
index eb0e850..0f29b41 100644
--- a/ambari-web/app/controllers/main/service/info/summary.js
+++ b/ambari-web/app/controllers/main/service/info/summary.js
@@ -803,7 +803,7 @@ App.MainServiceInfoSummaryController = Em.Controller.extend(App.WidgetSectionMix
           var view = this;
           var services = App.Service.find().filter(function(item){
             var stackService =  App.StackService.find().findProperty('serviceName', item.get('serviceName'));
-            return stackService.get('isServiceWithWidgets');
+            return stackService.get('isServiceWithWidgets') && item.get('serviceGroupName') === 'CORE';
           });
           return services.map(function (service) {
             return Em.Object.create({

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js b/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js
index e833ead..677bc05 100644
--- a/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js
+++ b/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js
@@ -210,7 +210,7 @@ App.WidgetWizardController = App.WizardController.extend({
       data: {
         stackVersionURL: App.get('stackVersionURL'),
         serviceNames: App.Service.find().filter(function (item) {
-          return App.StackService.find(item.get('id')).get('isServiceWithWidgets');
+          return App.StackService.find(item.get('id')).get('isServiceWithWidgets') && item.get('serviceGroupName') === 'CORE';
         }).mapProperty('serviceName').join(',')
       },
       callback: callback,

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/mappers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers.js b/ambari-web/app/mappers.js
index 96193bc..5937536 100644
--- a/ambari-web/app/mappers.js
+++ b/ambari-web/app/mappers.js
@@ -32,6 +32,7 @@ require('mappers/cluster_mapper');
 require('mappers/racks_mapper');
 require('mappers/users_mapper');
 require('mappers/service_mapper');
+require('mappers/service_group_mapper');
 require('mappers/service_metrics_mapper');
 require('mappers/target_cluster_mapper');
 require('mappers/component_config_mapper');

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/mappers/components_state_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/components_state_mapper.js b/ambari-web/app/mappers/components_state_mapper.js
index eb9ebd5..0c2d45c 100644
--- a/ambari-web/app/mappers/components_state_mapper.js
+++ b/ambari-web/app/mappers/components_state_mapper.js
@@ -24,7 +24,7 @@ App.componentsStateMapper = App.QuickDataMapper.create({
 
   clientModel: App.ClientComponent,
   clientMap: {
-    id: 'ServiceComponentInfo.component_name',
+    id: 'ServiceComponentInfo.id',
     service_id: 'ServiceComponentInfo.service_name',
     stack_info_id: 'ServiceComponentInfo.component_name',
     component_name: 'ServiceComponentInfo.component_name',
@@ -167,6 +167,7 @@ App.componentsStateMapper = App.QuickDataMapper.create({
 
     if (json.items) {
       json.items.forEach(function (item) {
+        item.ServiceComponentInfo.id =  item.ServiceComponentInfo.service_name + '_' + item.ServiceComponentInfo.component_name;
         var componentConfig = this.getComponentConfig(item.ServiceComponentInfo.component_name);
         var parsedItem = this.parseIt(item, componentConfig);
         var service = App.Service.find(item.ServiceComponentInfo.service_name);
@@ -185,9 +186,15 @@ App.componentsStateMapper = App.QuickDataMapper.create({
           masters.push(this.parseIt(item, this.clientMap));
         }
         if (cacheService) {
-          cacheService.client_components = clients.filterProperty('service_name', cacheService.ServiceInfo.service_name).mapProperty('component_name');
-          cacheService.slave_components = slaves.filterProperty('service_name', cacheService.ServiceInfo.service_name).mapProperty('component_name');
-          cacheService.master_components = masters.filterProperty('service_name', cacheService.ServiceInfo.service_name).mapProperty('component_name');
+          cacheService.client_components = clients.filterProperty('service_name', cacheService.ServiceInfo.service_name).map(function(item){
+            return cacheService.ServiceInfo.service_name + '_' + item.component_name;
+          });
+          cacheService.slave_components = slaves.filterProperty('service_name', cacheService.ServiceInfo.service_name).map(function(item){
+            return cacheService.ServiceInfo.service_name + '_' + item.component_name;
+          });
+          cacheService.master_components = masters.filterProperty('service_name', cacheService.ServiceInfo.service_name).map(function(item){
+            return cacheService.ServiceInfo.service_name + '_' + item.component_name;
+          });
           for (var i in parsedItem) {
             if (service.get('isLoaded')) {
               cacheService[i] = parsedItem[i];

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/mappers/service_group_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/service_group_mapper.js b/ambari-web/app/mappers/service_group_mapper.js
new file mode 100644
index 0000000..3591619
--- /dev/null
+++ b/ambari-web/app/mappers/service_group_mapper.js
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+var App = require('app');
+
+App.serviceGroupMapper = App.QuickDataMapper.create({
+  model: App.ServiceGroup,
+  config: {
+    id: 'ServiceGroupInfo.service_group_name',
+    cluster_name: 'ServiceGroupInfo.cluster_name',
+    service_group_name: 'ServiceGroupInfo.service_group_name',
+    service_group_display_name: 'ServiceGroupInfo.service_group_display_name',
+    service_group_type: 'ServiceGroupInfo.service_group_type',
+    current_state: 'ServiceGroupInfo.current_state',
+    desired_state: 'ServiceGroupInfo.desired_state',
+    assembly_file: 'ServiceGroupInfo.assembly_file',
+    application_id: 'ServiceGroupInfo.application_id',
+    quicklinks: 'ServiceGroupInfo.quicklinks',
+    lifetime: 'ServiceGroupInfo.lifetime',
+    containers_key: 'ServiceGroupInfo.containers',
+    containers_type: 'array',
+    containers: {
+      item: ''
+    },
+    services_key: 'services',
+    services_type: 'array',
+    services: {
+      item: 'id'
+    }
+  },
+
+  mapServiceGroups: function(json) {
+    this.clearStackModels();
+    App.resetDsStoreTypeMap(App.ServiceGroup);
+    this.map(json);
+  },
+
+  map: function (json) {
+    console.time("App.serviceGroupMapper execution time");
+    var self = this;
+    var displayOrderLength = App.ServiceGroup.displayOrder.length;
+    var items = json.items.map(function (item, index) {
+      var displayOrderIndex = App.ServiceGroup.displayOrder.indexOf(item.ServiceGroupInfo.service_group_display_name);
+      return $.extend(item, {
+        index: displayOrderIndex == -1 ? displayOrderLength + index : displayOrderIndex
+      });
+    }).sortProperty('index');
+
+
+
+    var item = {
+      ServiceGroupInfo: {
+        cluster_name: App.get('clusterName'),
+        containers: [],
+        service_group_name: "CORE"
+      }
+    };
+    items.unshift(item);
+    var serviceGroups = items.map(function (serviceGroup) {
+      var services = [];
+      if (serviceGroup.services) {
+        services = serviceGroup.services.map(function(service) {
+          return {id: service.ServiceInfo.service_name};
+        });
+      }
+      else {
+        //TODO remove after server API will be implemented
+        services = App.Service.find()
+          .filterProperty('serviceGroupName', serviceGroup.ServiceGroupInfo.service_group_name)
+          .map(function(service) {
+            return {
+              id: service.get('id')
+            }
+          });
+      }
+      serviceGroup.services = services;
+      return self.parseIt(serviceGroup, self.get('config'));
+    });
+    App.store.loadMany(this.get('model'), serviceGroups);
+    App.store.commit();
+
+    console.timeEnd("App.serviceGroupMapper execution time");
+  },
+
+  getJsonProperty: function (json, path) {
+    if (!path) {
+      return json;
+    }
+    return this._super(json, path);
+  },
+
+  /**
+   * Clean store from already loaded data.
+   **/
+  clearStackModels: function () {
+    var models = [App.ServiceGroup];
+    models.forEach(function (model) {
+      var records = App.get('store').findAll(model).filterProperty('id');
+      records.forEach(function (rec) {
+        Ember.run(this, function () {
+          rec.deleteRecord();
+          App.store.commit();
+        });
+      }, this);
+    }, this);
+  }
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/mappers/service_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/service_mapper.js b/ambari-web/app/mappers/service_mapper.js
index 9c21789..f5d5c6a 100644
--- a/ambari-web/app/mappers/service_mapper.js
+++ b/ambari-web/app/mappers/service_mapper.js
@@ -23,7 +23,10 @@ App.serviceMapper = App.QuickDataMapper.create({
   config: {
     id: 'ServiceInfo.service_name',
     service_name: 'ServiceInfo.service_name',
-    work_status: 'ServiceInfo.state'
+    work_status: 'ServiceInfo.state',
+    stack_service_name: 'ServiceInfo.stack_service_name',
+    service_group_name: 'ServiceInfo.service_group_name',
+    service_group_id: 'ServiceInfo.service_group_name'
   },
   initialAppLoad: false,
   passiveStateMap: {},
@@ -41,7 +44,10 @@ App.serviceMapper = App.QuickDataMapper.create({
         var serviceData = {
           ServiceInfo: {
             service_name: service.ServiceInfo.service_name,
-            state: service.ServiceInfo.state
+            state: service.ServiceInfo.state,
+            stack_service_name: service.ServiceInfo.stack_service_name,
+            service_group_name: service.ServiceInfo.service_group_name,
+            service_group_id: service.ServiceInfo.service_group_name
           },
           host_components: [],
           components: []
@@ -56,7 +62,7 @@ App.serviceMapper = App.QuickDataMapper.create({
         App.serviceMetricsMapper.mapExtendedModel(item);
         return self.parseIt(item, self.get('config'));
       });
-      parsedCacheServices = misc.sortByOrder(App.StackService.find().mapProperty('serviceName'), parsedCacheServices);
+      parsedCacheServices = misc.sortByOrder(App.StackService.find().mapProperty('serviceName'), parsedCacheServices, 'stack_service_name');
       App.store.loadMany(this.get('model'), parsedCacheServices);
       App.store.commit();
       this.set('initialAppLoad', true);

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/mappers/service_metrics_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/service_metrics_mapper.js b/ambari-web/app/mappers/service_metrics_mapper.js
index d862df7..b0104dd 100644
--- a/ambari-web/app/mappers/service_metrics_mapper.js
+++ b/ambari-web/app/mappers/service_metrics_mapper.js
@@ -36,7 +36,10 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
     installed_clients: 'installed_clients',
     client_components: 'client_components',
     slave_components: 'slave_components',
-    master_components: 'master_components'
+    master_components: 'master_components',
+    stack_service_name: 'ServiceInfo.stack_service_name',
+    service_group_name: 'ServiceInfo.service_group_name',
+    service_group_id: 'ServiceInfo.service_group_name'
   },
   hdfsConfig: {
     version: 'nameNodeComponent.host_components[0].metrics.dfs.namenode.Version',
@@ -203,6 +206,7 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
           previousComponentPassiveStates[host_component.id] = host_component.HostRoles.maintenance_state;
           this.config3.ha_status = host_component.HostRoles.component_name == "HBASE_MASTER" ?
             'metrics.hbase.master.IsActiveMaster' : 'HostRoles.ha_state';
+          host_component.HostRoles.display_name = component.ServiceComponentInfo.display_name;
           var comp = this.parseIt(host_component, this.config3);
           comp.id = id;
           comp.service_id = serviceName;
@@ -248,7 +252,7 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
       }, this);
 
       var stackServices = App.StackService.find().mapProperty('serviceName');
-      result = misc.sortByOrder(stackServices, result);
+      result = misc.sortByOrder(stackServices, result, 'stack_service_name');
 
       //load services to model
       App.store.loadMany(this.get('model'), result);
@@ -356,7 +360,9 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
           }
         } else if (hostComponent.component_name === 'HBASE_MASTER') {
           if (hostComponent.work_status === 'STARTED') {
-            (hostComponent.ha_status === true || hostComponent.ha_status == 'true') ?
+            //@TODO: Remove below consition when ambari-server supports returning HBase master HA status for assemblies
+            var isNoneCoreService = !App.StackService.find().mapProperty('serviceName').contains(hostComponent.service_id);
+            (hostComponent.ha_status === true || hostComponent.ha_status == 'true' || isNoneCoreService) ?
               hostComponent.display_name_advanced = this.t('dashboard.services.hbase.masterServer.active') :
               hostComponent.display_name_advanced = this.t('dashboard.services.hbase.masterServer.standby');
           } else {

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index bcb7ca3..e5e6489 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -180,6 +180,7 @@ Em.I18n.translations = {
   'common.error': 'Error',
   'common.loading': 'Loading',
   'common.search': 'Search',
+  'common.search.small': 'search',
   'common.confirm': 'Confirm',
   'common.upgrade': 'Upgrade',
   'common.reUpgrade': 'Retry Upgrade',
@@ -272,6 +273,7 @@ Em.I18n.translations = {
   'common.property.undefined': "Undefined",
   'common.summary': "Summary",
   'common.configs': "Configs",
+  'common.settings': 'Settings',
   'common.configuration': "Configuration",
   'common.unknown': "Unknown",
   'common.install': "Install",
@@ -330,6 +332,13 @@ Em.I18n.translations = {
   'common.logs': 'Logs',
   'common.warn.message': '<div class="alert alert-warn">{0}</div>',
   'common.link': 'Link',
+  'common.categories': 'Categories',
+  'common.manage': 'Manage',
+  'common.deploying': 'Deploying...',
+  'common.state': 'State',
+  'common.uri': 'URI',
+  'common.hostName': 'HostName',
+  'common.ip': 'IP',
 
   'models.alert_instance.tiggered.verbose': "Occurred on {0} <br> Checked on {1}",
   'models.alert_definition.triggered.verbose': "Occurred on {0}",
@@ -1027,6 +1036,38 @@ Em.I18n.translations = {
   'form.validator.alertNotificationName':'Invalid Alert Notification Name. Only alphanumerics, hyphens, spaces and underscores are allowed.',
   'form.validator.configKey.specific':'"{0}" is invalid Key. Only alphanumerics, hyphens, underscores, asterisks and periods are allowed.',
 
+  'appStore.menu.header.discover': 'Discover',
+  'appStore.menu.header.manage': 'Manage',
+  'appStore.collections.header': 'Collections',
+  'appStore.collections.addToCollection': 'My Collection',
+  'appStore.collections.assemblies': 'My Assemblies',
+  'appStore.apps.title.assemblies': 'assemblies',
+  'appStore.apps.title.components': 'components',
+  'appStore.apps.title.assemblies.tooltip': 'Complex YARN application consists of more than one components',
+  'appStore.apps.title.components.tooltip': 'Simple YARN application consists of only one component',
+  'appStore.apps.noApps': 'No Assemblies and Components',
+  'assembly.manage.summary.description': 'Credit and Payment card fraud has mushroomed into a massive challenge for consumers, financial institutions, regulators and law inforcements',
+  'assembly.manage.summary.logsearch.description': 'Log Search assembly helps to do analysis, monitor and visualization of the aggregated log of distributed systems.',
+  'assembly.manage.summary.hbase.description': 'Apache HBase is an open source NoSQL database that provides real-time read/write access to large datasets.',
+  'assembly.manage.summary.zookeeper.description': 'Apache ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.',
+  'assembly.manage.summary.custom.description': 'This is a custom community certified assembly.',
+  'assembly.manage.summary.label.service': 'SERVICES',
+  'assembly.manage.summary.business.impact': 'BUSINESS IMPACT',
+  'assembly.manage.summary.quick.links': 'QUICK LINKS',
+  'assembly.manage.summary.yarn.ui.quick.link': 'Assembly Details (YARN UI)',
+  'assembly.manage.summary.grafana.quick.link': 'Assembly Dashboard (Grafana)',
+  'assembly.manage.assemblySpecFile': 'Assembly Spec File',
+  'assembly.manage.fraudTransactions': 'fraud transactions',
+  'assembly.manage.fraudCost': 'fraud cost in dollars',
+
+  'assembly.detailedInfo.infoTitle.assemblyName': 'assembly name',
+  'assembly.detailedInfo.infoTitle.uri': 'uri',
+  'assembly.detailedInfo.infoTitle.lifetime': 'lifetime',
+  'assembly.detailedInfo.infoTitle.expectedContainers': 'expected containers',
+  'assembly.detailedInfo.infoTitle.containers': 'containers',
+  'assembly.detailedInfo.containers.launchTime': 'Launch Time',
+  'assembly.detailedInfo.containers.bareHost': 'Bare Host',
+
   'alerts.add.header': 'Create Alert Definition',
   'alerts.add.step1.header': 'Choose Type',
   'alerts.add.step2.header': 'Configure',
@@ -2049,8 +2090,12 @@ Em.I18n.translations = {
   'services.service.startAll':'Start All',
   'services.service.stopAll':'Stop All',
   'services.service.restartAllRequired':'Restart All Required',
-  'services.service.startAll.confirmMsg' : 'You are about to start all services',
-  'services.service.stopAll.confirmMsg' : 'You are about to stop all services',
+  'services.service.core.startAll.confirmMsg' : 'You are about to start all core services',
+  'services.service.cluster.startAll.confirmMsg' : 'You are about to start all services for cluster',
+  'services.service.startAll.confirmMsg' : 'You are about to start all services for {0} assembly',
+  'services.service.core.stopAll.confirmMsg' : 'You are about to stop all core services',
+  'services.service.cluster.stopAll.confirmMsg' : 'You are about to stop all services for cluster',
+  'services.service.stopAll.confirmMsg' : 'You are about to stop all services for {0} assembly',
   'services.service.refreshAll.confirmMsg': '<p>You are about to restart {0}</p>' +
     '<div class="alert alert-warning">This will trigger alerts as the service is restarted. To suppress alerts, turn on Maintenance Mode for services listed above prior to running Restart All Required</div>',
   'services.service.start.confirmMsg' : 'You are about to start {0}',
@@ -2929,9 +2974,20 @@ Em.I18n.translations = {
   'menu.item.hosts':'Hosts',
   'menu.item.admin':'Admin',
   'menu.item.alerts': 'Alerts',
+  'menu.item.appStore': 'Assemblies',
   'menu.item.views':'<i class="icon-th"></i>',
   'menu.item.views.noViews':'No Views',
 
+  'assemblies.app.deploy.popup.description': ' We need a few configuration details',
+  'assemblies.app.deploy.popup.config.maxTransactions': 'Max. Transactions (per second)',
+  'assemblies.app.deploy.popup.config.maxAnalysts': 'Max. Analysts',
+
+  'assemblies.app.deploy.popup.logsearh.config.SourceComponents': 'Source Components',
+  'assemblies.app.deploy.popup.logsearch.config.logEvents': 'Log Events (per second)',
+
+
+  'assemblies.serviceGroups.menu.detailedInfo': 'Detailed Info',
+
   'bulkOperation.loading': 'Loading...',
   'jobs.nothingToShow': 'No jobs to display',
   'jobs.loadingTasks': 'Loading...',

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/mixins/common/widgets/widget_section.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/widgets/widget_section.js b/ambari-web/app/mixins/common/widgets/widget_section.js
index abd0c97..f82d739 100644
--- a/ambari-web/app/mixins/common/widgets/widget_section.js
+++ b/ambari-web/app/mixins/common/widgets/widget_section.js
@@ -72,14 +72,15 @@ App.WidgetSectionMixin = Ember.Mixin.create({
    */
   isServiceWithEnhancedWidgets: function () {
     var isServiceWithWidgetdescriptor;
-    var serviceName = this.get('content.serviceName');
+    var serviceName = this.get('content.stackServiceName');
+    var isCore = this.get('content.serviceGroupName') === 'CORE';
     if (serviceName) {
-      isServiceWithWidgetdescriptor = App.StackService.find().findProperty('serviceName', serviceName).get('isServiceWithWidgets');
+      isServiceWithWidgetdescriptor = App.StackService.find().findProperty('serviceName', serviceName).get('isServiceWithWidgets') && isCore;
     } else if (this.get('sectionName') === 'SYSTEM_HEATMAPS') {
       isServiceWithWidgetdescriptor = true;
     }
     return isServiceWithWidgetdescriptor;
-  }.property('content.serviceName'),
+  }.property('content.stackServiceName'),
 
   /**
    *  @Type {App.WidgetLayout}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/models.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models.js b/ambari-web/app/models.js
index b877255..6a54fc2 100644
--- a/ambari-web/app/models.js
+++ b/ambari-web/app/models.js
@@ -37,6 +37,7 @@ require('models/stack_service_component');
 require('models/quick_links');
 require('models/quicklinks/quick_links_config');
 require('models/service');
+require('models/service_group');
 require('models/service_audit');
 require('models/service/hdfs');
 require('models/service/yarn');
@@ -51,6 +52,9 @@ require('models/alerts/alert_instance_local');
 require('models/alerts/alert_notification');
 require('models/alerts/alert_config');
 require('models/alerts/alert_group');
+require('models/app_store/store_app');
+require('models/app_store/store_category');
+require('models/app_store/store_collection');
 require('models/user');
 require('models/host');
 require('models/rack');

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/models/app_store/store_app.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/app_store/store_app.js b/ambari-web/app/models/app_store/store_app.js
new file mode 100644
index 0000000..654397d
--- /dev/null
+++ b/ambari-web/app/models/app_store/store_app.js
@@ -0,0 +1,268 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.StoreApp = DS.Model.extend({
+  name: DS.attr('string'),
+  title: DS.attr('string', {defaultValue: ''}),
+  logoUrl: DS.attr('string'),
+  description: DS.attr('string', {defaultValue: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam eget vehicula elit, ac sollicitudin enim. Curabitur ullamcorper vitae est at convallis. Curabitur et lacus vehicula, imperdiet sapien nec, tempus metus.\n\nPellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Etiam ut sapien leo. Quisque venenatis pellentesque erat, at auctor ipsum pulvinar ut.'}),
+  services: DS.attr('string'),
+  isActive: DS.attr('boolean'),
+  isComponent: DS.attr('boolean'),
+  storeCategories: DS.hasMany('App.StoreCategory'),
+  storeCollections: DS.hasMany('App.StoreCollection'),
+  configurations: DS.attr('array', {defaultValue:[{"name":Em.I18n.t('assemblies.app.deploy.popup.config.maxTransactions'),"minValue":100,"maxValue":100000,"step":100,"defaultValue":76000,"value":76000},{"name":Em.I18n.t('assemblies.app.deploy.popup.config.maxAnalysts'),"minValue":100,"maxValue":100000,"step":100,"defaultValue":300,"value":300}]}),
+
+  /**
+   * @type {boolean}
+   */
+  someConfigurationInvalid: function () {
+    return this.get('configurations').someProperty('errorMessage');
+  }.property('configurations.@each.errorMessage'),
+
+  init: function () {
+    this._super();
+    var configurations = this.get('configurations');
+    this.set('initConfigurations', JSON.parse(JSON.stringify(configurations)));
+  }
+
+});
+
+App.StoreApp.FIXTURES = [
+  {
+    id: 1,
+    name: 'Credit Fraud Detection',
+    title: 'Credit Fraud Detection with Apache NiFi',
+    logo_url: '/img/nifi-color.png',
+    services: 'HBASE | KAFKA | STORM | ZOOKEEPER',
+    is_active: false,
+    is_component: false,
+    description: 'Apache NiFi Eases Dataflow Management & Accelerates Time to Analytics in Banking.\n\nNiFi enables simple, fast data acquisition, secure data transport, prioritized data flow and clear traceability of data from the very edge of customer applications and endpoints all the way to the core data center.',
+    configurations: [
+      {
+        name: Em.I18n.t('assemblies.app.deploy.popup.config.maxTransactions'),
+        minValue: 100,
+        maxValue: 100000,
+        step: 100,
+        defaultValue: 76000,
+        value: 76000
+      },
+      {
+        name: Em.I18n.t('assemblies.app.deploy.popup.config.maxAnalysts'),
+        minValue: 100,
+        maxValue: 100000,
+        step: 100,
+        defaultValue: 300,
+        value: 300
+      }
+    ],
+    store_categories: [4],
+    store_collections: [1]
+  },
+  {
+    id: 2,
+    name: 'Log Search',
+    title: 'Log Search with Apache Solr',
+    logo_url: '/img/solr-color.png',
+    services: 'ZOOKEEPER | LOG SEARCH',
+    is_active: false,
+    is_component: false,
+    description: Em.I18n.t('assembly.manage.summary.logsearch.description') + "\n\nSolr is highly reliable, scalable and fault tolerant, providing distributed indexing, replication and load-balanced querying, automated failover and recovery, centralized configuration and more. Solr powers the search and navigation features of many of the world's largest internet sites.",
+    store_categories: [6],
+    store_collections: [],
+    configurations: [
+      {
+        name: Em.I18n.t('assemblies.app.deploy.popup.logsearh.config.SourceComponents'),
+        minValue: 1,
+        maxValue: 10000,
+        step: 100,
+        defaultValue: 1000,
+        value: 1000
+      },
+      {
+        name: Em.I18n.t('assemblies.app.deploy.popup.logsearch.config.logEvents'),
+        minValue: 100,
+        maxValue: 300000,
+        step: 100,
+        defaultValue: 150000,
+        value: 150000
+      }
+    ]
+  },
+  {
+    id: 3,
+    name: 'Apache Metron',
+    logo_url: '/img/metron-color.png',
+    services: 'HBASE | ACCUMULO | ZOOKEEPER',
+    is_active: false,
+    is_component: false,
+    description: 'Apache Metron is the next evolution of Security Incident Event Management.\nMetron helps users process unprecedented volumes of data per second, changing the game for malware detection and prevention. When an organization is attacked, Metron users can process and compare data from comprehensive feeds across the platform in real time. This not only facilitates enhanced detection of malware campaigns, but also impacts the economics for attackers by requiring them to customize malware for each target. ',
+    store_categories: [4],
+    store_collections: [1]
+  },
+  {
+    id: 4,
+    name: 'ETL in 1 Hour',
+    logo_url: '/img/sqoop-color.png',
+    services: 'SQOOP | OOZIE',
+    is_active: false,
+    is_component: false,
+    description: 'Sqoop is a command-line interface application for transferring data between relational databases and Hadoop.\n It supports incremental loads of a single table or a free form SQL query as well as saved jobs which can be run multiple times to import updates made to a database since the last import. Imports can also be used to populate tables in Hive or HBase.',
+    store_categories: [3],
+    store_collections: []
+  },
+  {
+    id: 5,
+    name: 'Backups',
+    logo_url: '/img/falcon-color.png',
+    services: 'KAFKA | STORM | ZOOKEEPER',
+    is_active: false,
+    is_component: false,
+    store_categories: [3],
+    store_collections: []
+  },
+  {
+    id: 6,
+    name: 'Prediction',
+    logo_url: '/img/spark-color.png',
+    services: 'HBASE | ACCUMULO | ZOOKEEPER',
+    is_active: false,
+    is_component: false,
+    store_categories: [6],
+    store_collections: []
+  },
+  {
+    id: 7,
+    name: 'Broker Mgmt',
+    logo_url: '/img/metron-color.png',
+    services: 'HBASE | ACCUMULO | ZOOKEEPER',
+    is_active: false,
+    is_component: false,
+    store_categories: [7],
+    store_collections: []
+  },
+  {
+    id: 8,
+    name: 'Configuration',
+    logo_url: '/img/storm-color.png',
+    services: 'HBASE | ACCUMULO | ZOOKEEPER',
+    is_active: false,
+    is_component: false,
+    store_categories: [3],
+    store_collections: []
+  },
+  {
+    id: 9,
+    name: 'Caching',
+    logo_url: '/img/memcache-color.png',
+    services: 'HBASE | ACCUMULO | ZOOKEEPER',
+    is_active: false,
+    is_component: false,
+    store_categories: [4],
+    store_collections: []
+  },
+  {
+    id: 10,
+    name: 'Fast Retrieval',
+    logo_url: '/img/accumulo-color.png',
+    services: 'HBASE | ACCUMULO | ZOOKEEPER',
+    is_active: false,
+    is_component: false,
+    store_categories: [6],
+    store_collections: []
+  },
+  {
+    id: 11,
+    name: 'Apache HBase',
+    logo_url: '/img/hbase-color.png',
+    is_active: false,
+    services: [],
+    is_component: true,
+    description: 'Apache HBase is an open source, non-relational, distributed database. \nHBase features compression, in-memory operation, and Bloom filters on a per-column basis as outlined in the original BigTable paper. Tables in HBase can serve as the input and output for MapReduce jobs run in Hadoop, and may be accessed through the Java API but also through REST, Avro or Thrift gateway APIs. ',
+    store_categories: [7],
+    store_collections: [1]
+  },
+  {
+    id: 12,
+    name: 'Apache Storm',
+    logo_url: '/img/storm-color.png',
+    is_active: false,
+    services: [],
+    is_component: true,
+    description: 'Apache Storm is a distributed computation framework written predominantly in the Clojure programming language.\n Originally created by Nathan Marz and team at BackType, the project was open sourced after being acquired by Twitter. It uses custom created "spouts" and "bolts" to define information sources and manipulations to allow batch, distributed processing of streaming data.',
+    store_categories: [3],
+    store_collections: [1]
+  },
+  {
+    id: 13,
+    name: 'Jenkins',
+    logo_url: '/img/jenkins-color.png',
+    is_active: false,
+    services: [],
+    is_component: true,
+    description: 'Jenkins is an open source continuous integration tool written in Java.\nJenkins provides continuous integration services for software development. It is a server-based system running in a servlet container such as Apache Tomcat. It supports SCM tools including AccuRev, CVS, Subversion, Git, Mercurial, Perforce, Clearcase and RTC, and can execute Apache Ant and Apache Maven based projects as well as arbitrary shell scripts and Windows batch commands.',
+    store_categories: [6],
+    store_collections: []
+  },
+  {
+    id: 14,
+    name: 'Apache Kafka',
+    logo_url: '/img/kafka-color.png',
+    is_active: false,
+    services: [],
+    is_component: true,
+    description: 'Apache Kafka is an open-source message broker project developed by the Apache Software Foundation written in Scala.\n The project aims to provide a unified, high-throughput, low-latency platform for handling real-time data feeds. It is, in its essence, a "massively scalable pub/sub message queue architected as a distributed transaction log", making it highly valuable for enterprise infrastructures.',
+    store_categories: [7],
+    store_collections: [1]
+  },
+  {
+    id: 15,
+    name: 'Apache Spark',
+    logo_url: '/img/spark-color.png',
+    is_active: false,
+    services: [],
+    is_component: true,
+    description: 'Apache Spark provides programmers with an application programming interface centered on a data structure called the resilient distributed dataset (RDD), a read-only multiset of data items distributed over a cluster of machines, that is maintained in a fault-tolerant way.\n It was developed in response to limitations in the MapReduce cluster computing paradigm, which forces a particular linear dataflow structure on distributed programs.',
+    store_categories: [6],
+    store_collections: []
+  },
+  {
+    id: 16,
+    name: 'Apache Solr',
+    logo_url: '/img/solr-color.png',
+    is_active: false,
+    services: [],
+    is_component: true,
+    description: 'Solr (pronounced "solar") is an open source enterprise search platform, written in Java, from the Apache Lucene project.\n Its major features include full-text search, hit highlighting, faceted search, real-time indexing, dynamic clustering, database integration, NoSQL features and rich document (e.g., Word, PDF) handling.',
+    store_categories: [6],
+    store_collections: []
+  },
+  {
+    id: 17,
+    name: 'ZooKeeper',
+    logo_url: '/img/zookeeper-color.png',
+    is_active: false,
+    services: [],
+    is_component: true,
+    description: 'ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.\n All of these kinds of services are used in some form or another by distributed applications. Each time they are implemented there is a lot of work that goes into fixing the bugs and race conditions that are inevitable.',
+    store_categories: [6],
+    store_collections: []
+  }
+];
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/models/app_store/store_category.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/app_store/store_category.js b/ambari-web/app/models/app_store/store_category.js
new file mode 100644
index 0000000..a086d99
--- /dev/null
+++ b/ambari-web/app/models/app_store/store_category.js
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.StoreCategory = DS.Model.extend({
+
+  name: DS.attr('string'),
+  isActive: DS.attr('boolean'),
+  storeApps: DS.hasMany('App.StoreApp')
+
+});
+
+App.StoreCategory.FIXTURES = [
+  {id: 1, name: 'All', is_active: false, store_apps: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]},
+  {id: 2, name: 'Access', is_active: false, store_apps: []},
+  {id: 3, name: 'Data Movement', is_active: false, store_apps: [4, 5, 8, 12]},
+  {id: 4, name: 'Security', is_active: false, store_apps: [1, 3, 9]},
+  {id: 5, name: 'Governance', is_active: false, store_apps: []},
+  {id: 6, name: 'Operations', is_active: false, store_apps: [2, 6, 10, 11, 13, 15, 16, 17]},
+  {id: 7, name: 'Storage', is_active: false, store_apps: [7, 14]}
+];
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/models/app_store/store_collection.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/app_store/store_collection.js b/ambari-web/app/models/app_store/store_collection.js
new file mode 100644
index 0000000..c6b46c0
--- /dev/null
+++ b/ambari-web/app/models/app_store/store_collection.js
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.StoreCollection = DS.Model.extend({
+
+  name: DS.attr('string'),
+  isActive: DS.attr('boolean'),
+  storeApps: DS.hasMany('App.StoreApp')
+
+});
+
+App.StoreCollection.FIXTURES = [
+  {id: 1, name: 'Manage My System', store_apps: [1, 3, 11, 12], is_active: false}
+];
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/models/service.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/service.js b/ambari-web/app/models/service.js
index c648e93..da04f6a 100644
--- a/ambari-web/app/models/service.js
+++ b/ambari-web/app/models/service.js
@@ -22,13 +22,16 @@ require('utils/config');
 
 App.Service = DS.Model.extend({
   serviceName: DS.attr('string'),
-  displayName: Em.computed.formatRole('serviceName', true),
+  displayName: Em.computed.formatRole('stackServiceName', true),
+  stackServiceName: DS.attr('string'),
+  serviceGroupName: DS.attr('string'),
   passiveState: DS.attr('string'),
   workStatus: DS.attr('string'),
   rand: DS.attr('string'),
   toolTipContent: DS.attr('string'),
   quickLinks: DS.hasMany('App.QuickLinks'),  // mapped in app/mappers/service_metrics_mapper.js method - mapQuickLinks
   hostComponents: DS.hasMany('App.HostComponent'),
+  serviceGroup: DS.belongsTo('App.ServiceGroup'),
   serviceConfigsTemplate: App.config.get('preDefinedServiceConfigs'),
   /**
    * used by services("OOZIE", "ZOOKEEPER", "HIVE", "MAPREDUCE2", "TEZ", "SQOOP", "PIG","FALCON")
@@ -165,7 +168,14 @@ App.Service = DS.Model.extend({
    * Number of the Critical and Warning alerts for current service
    * @type {number}
    */
-  alertsCount: 0
+  alertsCount: 0,
+
+  /**
+   *
+   */
+  logo: function() {
+    return App.Service.logo[this.get('stackServiceName')];
+  }.property('id')
 
 });
 
@@ -251,3 +261,18 @@ App.Service.extendedModel = {
 };
 
 App.Service.FIXTURES = [];
+
+App.Service.logo = {
+  'NIFI': '/img/nifi-color.png',
+  'CFMON': '/img/cfmon-color.png',
+  'LOGSEARCH': '/img/solr-color.png',
+  'SQOOP':'/img/sqoop-color.png',
+  'FALCON':'/img/falcon-color.png',
+  'SPARK':'/img/spark-color.png',
+  'SPARK2':'/img/spark-color.png',
+  'STORM':'/img/storm-color.png',
+  'ACCUMULO':'/img/accumulo-color.png',
+  'HBASE':'/img/hbase-color.png',
+  'ZOOKEEPER':'/img/zookeeper-color.png',
+  'KAFKA':'/img/kafka-color.png'
+};

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/models/service_group.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/service_group.js b/ambari-web/app/models/service_group.js
new file mode 100644
index 0000000..532f1d3
--- /dev/null
+++ b/ambari-web/app/models/service_group.js
@@ -0,0 +1,270 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+var App = require('app');
+
+App.ServiceGroup = DS.Model.extend({
+  clusterName: DS.attr('string'),
+  serviceGroupName: DS.attr('string'),
+  serviceGroupDisplayName: DS.attr('string'),
+  serviceGroupType: DS.attr('string'),
+  currentState: DS.attr('string'),
+  desiredState: DS.attr('string'),
+  assemblyFile: DS.attr('string'),
+  quicklinks: DS.attr('object'),
+  applicationId: DS.attr('string'),
+  lifetime: DS.attr('string'),
+  services: DS.hasMany('App.Service'),
+  isActive: false,
+
+  containers: DS.attr('object'),
+
+  businessImpact: {
+    fraudTransactions: 46,
+    fraudCost: '23,000'
+  },
+
+  configurations: function() {
+    var serviceMetaData = App.ServiceGroup.metadata[this.get('serviceGroupDisplayName')];
+    if (serviceMetaData && serviceMetaData.configurations) {
+      return serviceMetaData.configurations;
+    } else {
+      return App.ServiceGroup.metadata["Credit Fraud Detection"].configurations;
+    }
+  }.property('id'),
+
+  description : function() {
+    var serviceMetaData = App.ServiceGroup.metadata[this.get('serviceGroupDisplayName')];
+    if (serviceMetaData) {
+      return serviceMetaData.description;
+    } else {
+      return Em.I18n.t('assembly.manage.summary.custom.description');
+    }
+  }.property('id'),
+
+  /**
+   * @type {string}
+   */
+  serviceGroupNameLower: function () {
+    return (this.get('serviceGroupName') || '').toLowerCase();
+  }.property('serviceGroupName'),
+
+  /**
+   * @type {string}
+   */
+  uri: Em.computed.format('/services/v1/applications/{0}', 'serviceGroupNameLower'),
+
+  /**
+   * @type {number}
+   */
+  alertsCount: Em.computed.sumBy('services', 'alertsCount'),
+
+  /**
+   * @type {number|string}
+   */
+  shownAlertsCount: function () {
+    var alertsCount = this.get('alertsCount');
+    return alertsCount > 99 ? '99+' : alertsCount;
+  }.property('alertsCount'),
+
+  /**
+   * @type {boolean}
+   */
+  isStopped: Em.computed.equal('desiredState', 'INSTALLED'),
+
+  /**
+   * @type {boolean}
+   */
+  isStarted: Em.computed.equal('desiredState', 'STARTED'),
+
+  /**
+   * Should "Start All"-button be disabled
+   * @type {bool}
+   */
+  isStartAllDisabled: Em.computed.or('isStartStopAllClicked', '!someServiceIsStopped'),
+
+  /**
+   * @type {boolean}
+   */
+  someServiceIsStopped: function () {
+    return this.get('services').filter(function (_service) {
+        return _service.get('healthStatus') === 'red' && !App.get('services.clientOnly').contains(_service.get('serviceName'));
+      }).length > 0;
+  }.property('services.@each.healthStatus'),
+
+  /**
+   * Should "Stop All"-button be disabled
+   * @type {bool}
+   */
+  isStopAllDisabled: Em.computed.or('isStartStopAllClicked', '!someServiceIsStarted'),
+
+  /**
+   * @type {boolean}
+   */
+  someServiceIsStarted: Em.computed.someBy('services', 'healthStatus', 'green'),
+
+  /**
+   * @type {boolean}
+   */
+  allServicesAreStarted: Em.computed.everyBy('services', 'healthStatus', 'green'),
+
+  /**
+   * Should "Refresh All"-button be disabled
+   * @type {bool}
+   */
+  isRestartAllRequiredDisabled: Em.computed.everyBy('services', 'isRestartRequired', false),
+
+  /**
+   * @type {bool}
+   */
+  isStartStopAllClicked: Em.computed.notEqual('App.router.backgroundOperationsController.allOperationsCount', 0),
+
+  /**
+   * Check if all services are installed
+   * true - all installed, false - not all
+   * @type {bool}
+   */
+  isAllServicesInstalled: function () {
+    var sLength = this.get('services.content.length');
+    if (!sLength) return false;
+    var availableServices = App.StackService.find().mapProperty('serviceName');
+    return sLength === availableServices.length;
+  }.property('services.[]', 'services.content.length'),
+
+  /**
+   * Check if Core service group
+   * true - all installed, false - not all
+   * @type {bool}
+   */
+  isCoreServiceGroup: Em.computed.equal('serviceGroupName', 'CORE'),
+
+  /**
+   * Check if CF-MONITOR service group
+   * true - all installed, false - not all
+   * @type {bool}
+   */
+  isCfMonitorServiceGroup: Em.computed.equal('serviceGroupDisplayName', 'Credit Fraud Detection'),
+
+  /**
+   * @type {boolean}
+   */
+  someConfigurationInvalid: function () {
+    return this.get('configurations').someProperty('errorMessage');
+  }.property('configurations.@each.errorMessage'),
+
+  init: function () {
+    this._super();
+    var configurations = this.get('configurations');
+    this.set('initConfigurations', JSON.parse(JSON.stringify(configurations)));
+  },
+
+  assemblyLinks: function() {
+    var quickLinks = [];
+    var yarnService = App.YARNService.find().objectAt(0);
+    if (yarnService) {
+      var activeRm = yarnService.get('hostComponents').filterProperty('componentName', 'RESOURCEMANAGER').findProperty('haStatus', 'ACTIVE');
+      if (activeRm) {
+        var activeRmHostName = activeRm.get('hostName');
+        var applicationLink = 'http://' + activeRmHostName + ':8088/proxy/' + this.get('applicationId') ;
+        var YarnLinkObject = {
+          label: Em.I18n.t('assembly.manage.summary.yarn.ui.quick.link'),
+          url: applicationLink
+        };
+        quickLinks.pushObject(YarnLinkObject);
+      }
+    }
+    if (this.get('isCfMonitorServiceGroup')) {
+      var grafanaLinkObject = {
+        label: Em.I18n.t('assembly.manage.summary.grafana.quick.link'),
+        url: 'http://cn005.l42scl.hortonworks.com:3000/dashboard/db/cf-monitor'
+      };
+      quickLinks.pushObject(grafanaLinkObject);
+    }
+    var componentLinks = this.get('quicklinks');
+    if (!Em.isNone(componentLinks)) {
+      Object.keys(componentLinks).forEach(function (key) {
+        var value = componentLinks[key];
+        quickLinks.pushObject({
+          label: key,
+          url: value
+        });
+      }, this);
+    }
+    return quickLinks;
+  }.property('App.router.clusterController.isServiceMetricsLoaded', 'serviceGroupName')
+
+});
+
+App.ServiceGroup.FIXTURES = [];
+
+App.ServiceGroup.displayOrder = [
+  'Credit Fraud Detection',
+  'Logsearch Stable'
+];
+
+App.ServiceGroup.metadata = {
+  "Credit Fraud Detection": {
+    description: Em.I18n.t('assembly.manage.summary.description'),
+    configurations: [
+      {
+        name: Em.I18n.t('assemblies.app.deploy.popup.config.maxTransactions'),
+        minValue: 100,
+        maxValue: 100000,
+        step: 100,
+        defaultValue: 76000,
+        value: 76000
+      },
+      {
+        name: Em.I18n.t('assemblies.app.deploy.popup.config.maxAnalysts'),
+        minValue: 100,
+        maxValue: 100000,
+        step: 100,
+        defaultValue: 300,
+        value: 300
+      }
+    ]
+  },
+  "Logsearch Stable": {
+    description: Em.I18n.t('assembly.manage.summary.logsearch.description'),
+    configurations: [
+      {
+        name: Em.I18n.t('assemblies.app.deploy.popup.logsearh.config.SourceComponents'),
+        minValue: 1,
+        maxValue: 10000,
+        step: 100,
+        defaultValue: 1000,
+        value: 1000
+      },
+      {
+        name: Em.I18n.t('assemblies.app.deploy.popup.logsearch.config.logEvents'),
+        minValue: 100,
+        maxValue: 300000,
+        step: 100,
+        defaultValue: 150000,
+        value: 150000
+      }
+    ]
+  },
+  "HBase Stable": {
+    description: Em.I18n.t('assembly.manage.summary.hbase.description')
+  },
+  "Vinod's Zookeeper": {
+    description: Em.I18n.t('assembly.manage.summary.zookeeper.description')
+  }
+};

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/router.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/router.js b/ambari-web/app/router.js
index 496e3d3..30adb60 100644
--- a/ambari-web/app/router.js
+++ b/ambari-web/app/router.js
@@ -86,6 +86,13 @@ App.Router = Em.Router.extend({
    */
   preferedPath: null,
 
+  /**
+   * @type {boolean}
+   */
+  isAppStorePageSelected: function() {
+    return this.get('currentState.path').indexOf('main.assemblies') !== -1;
+  }.property('currentState.path'),
+
   setNavigationFlow: function (step) {
     var matches = step.match(/\d+$/);
     var newStep;

http://git-wip-us.apache.org/repos/asf/ambari/blob/b88db3cc/ambari-web/app/routes/main.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/routes/main.js b/ambari-web/app/routes/main.js
index fba3410..f622490 100644
--- a/ambari-web/app/routes/main.js
+++ b/ambari-web/app/routes/main.js
@@ -17,7 +17,6 @@
  */
 
 var App = require('app');
-var stringUtils = require('utils/string_utils');
 
 module.exports = Em.Route.extend(App.RouterRedirections, {
   route: '/main',
@@ -79,18 +78,6 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       }
     });
   },
-  /*
-   routePath: function(router,event) {
-   if (router.getAuthenticated()) {
-   App.router.get('clusterController').loadClusterName(false);
-   router.get('mainController').initialize();
-   // TODO: redirect to last known state
-   } else {
-   Ember.run.next(function () {
-   router.transitionTo('login');
-   });
-   }
-   }, */
 
   index: Ember.Route.extend({
     route: '/',
@@ -187,6 +174,151 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
   views: require('routes/views'),
   view: require('routes/view'),
 
+  assemblies: Em.Route.extend({
+    route: '/assemblies',
+
+    connectOutlets: function(router) {
+      router.get('mainController').connectOutlet('mainAssemblies');
+    },
+
+    index: Em.Route.extend({
+      route: '/',
+      enter: function (router) {
+        router.transitionTo('categories.index');
+      }
+    }),
+
+    categories: Em.Route.extend({
+      exit: function () {
+        App.StoreCategory.find().setEach('isActive', false);
+      },
+      route: '/categories',
+      index: Em.Route.extend({
+        route: '/',
+        enter: function (router) {
+          Em.run.next(function () {
+            var storeCategories = App.StoreCategory.find();
+            storeCategories.setEach('isActive', false);
+            var storeCategory = storeCategories.findProperty('name', 'All');
+            storeCategory.set('isActive',true);
+            router.transitionTo('categories.details', storeCategory);
+          });
+        },
+        connectOutlets: function (router) {
+          var controller = router.get('mainAssembliesController');
+          controller.set('showFilterString', true);
+          controller.connectOutlet('mainAssembliesCategories');
+        }
+      }),
+
+      details: Em.Route.extend({
+        route: '/:store_category_id',
+        connectOutlets: function (router, store_category) {
+          router.get('mainController').dataLoading().done(function () {
+            App.StoreCategory.find().setEach('isActive', false);
+            store_category.set('isActive', true);
+            router.get('mainAssembliesController').connectOutlet('mainAssembliesCategory', store_category);
+            router.get('mainAssembliesCategoryController').propertyDidChange('filteredStoreApps');
+          });
+        }
+      })
+    }),
+
+    collections: Em.Route.extend({
+      exit: function () {
+        App.StoreCollection.find().setEach('isActive', false);
+      },
+      route: '/collections',
+      index: Em.Route.extend({
+        route: '/',
+        connectOutlets: function (router) {
+          var controller = router.get('mainAssembliesController');
+          controller.set('showFilterString', true);
+          controller.connectOutlet('mainAssembliesCollections');
+        }
+      }),
+
+      details: Em.Route.extend({
+        route: '/:store_collection_id',
+        connectOutlets: function (router, store_collection) {
+          App.StoreCollection.find().setEach('isActive', false);
+          store_collection.set('isActive', true);
+          router.get('mainAssembliesController').connectOutlet('mainAssembliesCollection', store_collection);
+          router.get('mainAssembliesCollectionController').propertyDidChange('filteredStoreApps');
+        }
+      })
+    }),
+
+    serviceGroups: Em.Route.extend({
+      route: '/service-groups',
+      enter: function (router) {
+        router.get('mainAssembliesController').set('showFilterString', false);
+      },
+      exit: function (router) {
+        App.ServiceGroup.find().setEach('isActive', false);
+        router.get('mainAssembliesController').set('showFilterString', true);
+      },
+      index: Em.Route.extend({
+        route: '/',
+        connectOutlets: function (router) {
+          router.get('mainAssembliesController').connectOutlet('mainAssembliesServiceGroups');
+        }
+      }),
+      details: Em.Route.extend({
+        route: '/:service_group_id',
+
+        connectOutlets: function (router, service_group) {
+          router.get('mainController').dataLoading().done(function () {
+            var serviceGroup = App.ServiceGroup.find(service_group.get('id'));
+            router.get('mainAssembliesController').connectOutlet('mainAssembliesServiceGroup', serviceGroup);
+              router.transitionTo('summary');
+          });
+        },
+
+        index: Em.Route.extend({
+          route: '/'
+        }),
+
+        summary: Em.Route.extend({
+          route: '/summary',
+          connectOutlets: function (router) {
+            router.get('mainController').dataLoading().done(function () {
+              var serviceGroup =  router.get('mainAssembliesServiceGroupController.content');
+              var assembliesController = App.router.get('mainAssembliesController');
+              assembliesController.set('activeServiceGroupId', serviceGroup.get('id'));
+              Em.run.later(function () {
+                var appStoreEl = $("#apps-store");
+                if (appStoreEl) {
+                  assembliesController.closeNonServiceGroupsPanels();
+                  var deployedAssembliesEl = appStoreEl.find("#manage-deployed-assemblies-label");
+                  var deployedAssembliesContent = appStoreEl.find("#manage-deployed-assemblies-content");
+                  if (!deployedAssembliesContent.hasClass('in')) {
+                    deployedAssembliesEl.trigger('click');
+                  }
+                }
+              }, 1500);
+              router.get('mainAssembliesServiceGroupController').connectOutlet('mainAssembliesServiceGroupSummary', serviceGroup);
+            });
+          }
+        }),
+
+        settings: Em.Route.extend({
+          route: '/settings',
+          connectOutlets: function (router) {
+            router.get('mainAssembliesServiceGroupController').connectOutlet('mainAssembliesServiceGroupConfigs', router.get('mainAssembliesServiceGroupController.content'));
+          }
+        }),
+
+        detailedInfo: Em.Route.extend({
+          route: '/detailed-info',
+          connectOutlets: function (router) {
+            router.get('mainAssembliesServiceGroupController').connectOutlet('mainAssembliesServiceGroupDetailedInfo', router.get('mainAssembliesServiceGroupController.content'));
+          }
+        })
+      })
+    })
+
+  }),
 
   hosts: Em.Route.extend({
     route: '/hosts',
@@ -642,18 +774,24 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
     service: Em.Route.extend({
       route: '/:service_id',
       connectOutlets: function (router, service) {
-        router.get('mainServiceController').connectOutlet('mainServiceItem', service);
-        if (service.get('isLoaded')) {
-          if (router.get('mainServiceItemController').get('routeToConfigs')) {
-            router.transitionTo('configs');
-          } else if (router.get('mainServiceItemController.routeToHeatmaps')) {
-            router.transitionTo('heatmaps');
+        var controller = router.get('mainController');
+        controller.dataLoading().done(function () {
+          var mainServiceController = router.get('mainServiceController');
+          var serviceGroup = App.ServiceGroup.find().findProperty('id', service.get('serviceGroupName'));
+          mainServiceController.set('serviceGroup', serviceGroup);
+          router.get('mainServiceController').connectOutlet('mainServiceItem', service);
+          if (service.get('isLoaded')) {
+            if (router.get('mainServiceItemController').get('routeToConfigs')) {
+              router.transitionTo('configs');
+            } else if (router.get('mainServiceItemController.routeToHeatmaps')) {
+              router.transitionTo('heatmaps');
+            } else {
+              router.transitionTo('summary');
+            }
           } else {
-            router.transitionTo('summary');
+            router.transitionTo('index');
           }
-        } else {
-          router.transitionTo('index');
-        }
+        });
       },
       index: Ember.Route.extend({
         route: '/'
@@ -661,15 +799,18 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       summary: Em.Route.extend({
         route: '/summary',
         connectOutlets: function (router, context) {
-          App.loadTimer.start('Service Summary Page');
-          var item = router.get('mainServiceItemController.content');
-          if (router.get('clusterController.isServiceMetricsLoaded')) router.get('updateController').updateServiceMetric(Em.K);
-          //if service is not existed then route to default service
-          if (item.get('isLoaded')) {
-            router.get('mainServiceItemController').connectOutlet('mainServiceInfoSummary', item);
-          } else {
-            router.transitionTo('services.index');
-          }
+          var controller = router.get('mainController');
+          controller.dataLoading().done(function () {
+            App.loadTimer.start('Service Summary Page');
+            var item = router.get('mainServiceItemController.content');
+            if (router.get('clusterController.isServiceMetricsLoaded')) router.get('updateController').updateServiceMetric(Em.K);
+            //if service is not existed then route to default service
+            if (item.get('isLoaded')) {
+              router.get('mainServiceItemController').connectOutlet('mainServiceInfoSummary', item);
+            } else {
+              router.transitionTo('services.index');
+            }
+          });
         }
       }),
       metrics: Em.Route.extend({
@@ -773,7 +914,8 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
     router.transitionTo('hosts.hostDetails.index', event.context);
   },
   filterHosts: function (router, component) {
-    if (!component.context)
+    var serviceGroupName = component.context.get('service.serviceGroupName');
+    if (!component.context || (serviceGroupName && serviceGroupName !== 'CORE'))
       return;
     router.get('mainHostController').filterByComponent(component.context);
     router.get('mainHostController').set('showFilterConditionsFirstLoad', true);
@@ -781,9 +923,11 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
     router.transitionTo('hosts.index');
   },
   showDetails: function (router, event) {
-    router.get('mainHostDetailsController').set('referer', router.location.lastSetURL);
-    router.get('mainHostDetailsController').set('isFromHosts', true);
-    router.transitionTo('hosts.hostDetails.summary', event.context);
+    if (event.context && App.Host.find().mapProperty('id').contains(event.context.get('id'))) {
+      router.get('mainHostDetailsController').set('referer', router.location.lastSetURL);
+      router.get('mainHostDetailsController').set('isFromHosts', true);
+      router.transitionTo('hosts.hostDetails.summary', event.context);
+    }
   },
   gotoAlertDetails: function (router, event) {
     router.transitionTo('alerts.alertDetails', event.context);