浏览代码

AMBARI-16076. Add a quick link to Grafana dashboards from Hive Service page. (jaimin)

Jaimin Jetly 9 年之前
父节点
当前提交
d14654bf5f
共有 18 个文件被更改,包括 326 次插入151 次删除
  1. 26 0
      ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java
  2. 1 1
      ambari-server/src/main/resources/common-services/AMBARI_METRICS/0.1.0/quicklinks/quicklinks.json
  3. 1 0
      ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/quicklinks/quicklinks.json
  4. 1 0
      ambari-server/src/main/resources/common-services/STORM/0.9.1.2.1/quicklinks/quicklinks.json
  5. 6 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HBASE/quicklinks/quicklinks.json
  6. 4 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HDFS/quicklinks/quicklinks.json
  7. 1 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/quicklinks/quicklinks.json
  8. 4 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/YARN/quicklinks-mapred/quicklinks.json
  9. 4 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/YARN/quicklinks/quicklinks.json
  10. 1 0
      ambari-server/src/main/resources/stacks/HDP/2.2/services/RANGER/quicklinks/quicklinks.json
  11. 1 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/ACCUMULO/quicklinks/quicklinks.json
  12. 1 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/ATLAS/quicklinks/quicklinks.json
  13. 26 0
      ambari-server/src/main/resources/stacks/HDP/2.5/services/HIVE/quicklinks/quicklinks.json
  14. 10 1
      ambari-web/app/controllers/wizard/step6_controller.js
  15. 2 2
      ambari-web/app/controllers/wizard/step7_controller.js
  16. 1 19
      ambari-web/app/models/quicklinks/quick_links_config.js
  17. 63 44
      ambari-web/app/views/common/quick_view_link_view.js
  18. 173 84
      ambari-web/test/views/common/quick_link_view_test.js

+ 26 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java

@@ -31,6 +31,9 @@ public class Link{
   @JsonProperty("label")
   private String label;
 
+  @JsonProperty("component_name")
+  private String componentName;
+
   @JsonProperty("requires_user_name")
   private String requiresUserName;
 
@@ -40,6 +43,9 @@ public class Link{
   @JsonProperty("port")
   private Port port;
 
+  @JsonProperty("protocol")
+  private Protocol protocol;
+
   public String getName() {
     return name;
   }
@@ -56,6 +62,14 @@ public class Link{
     this.label = label;
   }
 
+  public String getComponentName() {
+    return componentName;
+  }
+
+  public void setComponentName(String componentName) {
+    this.componentName = componentName;
+  }
+
   public String getUrl() {
     return url;
   }
@@ -80,6 +94,15 @@ public class Link{
     this.port = port;
   }
 
+  public Protocol getProtocol() {
+    return protocol;
+  }
+
+  public void setProtocol(Protocol protocol) {
+    this.protocol = protocol;
+  }
+
+
   public boolean isRemoved(){
     //treat a link as removed if the section only contains a name
     return (null == port && null == url && null == label && null == requiresUserName);
@@ -95,6 +118,9 @@ public class Link{
     if(null == label && null != parentLink.getLabel())
       label = parentLink.getLabel();
 
+    if(null == componentName && null != parentLink.getComponentName())
+      componentName = parentLink.getComponentName();
+
     if(null == url && null != parentLink.getUrl())
       url = parentLink.getUrl();
 

+ 1 - 1
ambari-server/src/main/resources/common-services/AMBARI_METRICS/0.1.0/quicklinks/quicklinks.json

@@ -18,8 +18,8 @@
         "name": "metrics_ui_server",
         "label": "Grafana",
         "requires_user_name": "false",
+        "component_name": "METRICS_GRAFANA",
         "url":"%@://%@:%@",
-        "template":"%@://%@:%@",
         "port":{
           "http_property": "port",
           "http_default_port": "3000",

+ 1 - 0
ambari-server/src/main/resources/common-services/LOGSEARCH/0.5.0/quicklinks/quicklinks.json

@@ -9,6 +9,7 @@
       {
         "name": "logsearch_ui",
         "label": "Logsearch UI",
+        "component_name": "LOGSEARCH_SERVER",
         "url": "%@://%@:%@",
         "requires_user_name": "false",
         "port": {

+ 1 - 0
ambari-server/src/main/resources/common-services/STORM/0.9.1.2.1/quicklinks/quicklinks.json

@@ -12,6 +12,7 @@
         "name": "storm_ui",
         "label": "Storm UI",
         "requires_user_name": "false",
+        "component_name": "STORM_UI_SERVER",
         "url":"%@://%@:%@/",
         "port":{
           "http_property": "ui.port",

+ 6 - 0
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HBASE/quicklinks/quicklinks.json

@@ -11,6 +11,7 @@
       {
         "name": "hbase_master_ui",
         "label": "HBase Master UI",
+        "component_name": "HBASE_MASTER",
         "url":"%@://%@:%@/master-status",
         "requires_user_name": "false",
         "port":{
@@ -25,6 +26,7 @@
       {
         "name": "hbase_logs",
         "label": "HBase Logs",
+        "component_name": "HBASE_MASTER",
         "url":"%@://%@:%@/logs",
         "requires_user_name": "false",
         "port":{
@@ -39,6 +41,7 @@
       {
         "name": "zookeeper_info",
         "label": "Zookeeper Info",
+        "component_name": "HBASE_MASTER",
         "url":"%@://%@:%@/zk.jsp",
         "requires_user_name": "false",
         "port":{
@@ -53,6 +56,7 @@
       {
         "name": "hbase_master_jmx",
         "label": "HBase Master JMX",
+        "component_name": "HBASE_MASTER",
         "url":"%@://%@:%@/jmx",
         "requires_user_name": "false",
         "port":{
@@ -67,6 +71,7 @@
       {
         "name": "debug_dump",
         "label": "Debug Dump",
+        "component_name": "HBASE_MASTER",
         "url":"%@://%@:%@/dump",
         "requires_user_name": "false",
         "port":{
@@ -81,6 +86,7 @@
       {
         "name": "thread_stacks",
         "label": "Thread Stacks",
+        "component_name": "HBASE_MASTER",
         "url":"%@://%@:%@/stacks",
         "requires_user_name": "false",
         "port":{

+ 4 - 0
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HDFS/quicklinks/quicklinks.json

@@ -18,6 +18,7 @@
       {
         "name": "namenode_ui",
         "label": "NameNode UI",
+        "component_name": "NAMENODE",
         "url":"%@://%@:%@",
         "requires_user_name": "false",
         "port":{
@@ -32,6 +33,7 @@
       {
         "name": "namenode_logs",
         "label": "NameNode Logs",
+        "component_name": "NAMENODE",
         "url":"%@://%@:%@/logs",
         "requires_user_name": "false",
         "port":{
@@ -46,6 +48,7 @@
       {
         "name": "namenode_jmx",
         "label": "NameNode JMX",
+        "component_name": "NAMENODE",
         "url":"%@://%@:%@/jmx",
         "requires_user_name": "false",
         "port":{
@@ -60,6 +63,7 @@
       {
         "name": "Thread Stacks",
         "label": "Thread Stacks",
+        "component_name": "NAMENODE",
         "url":"%@://%@:%@/stacks",
         "requires_user_name": "false",
         "port":{

+ 1 - 0
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/quicklinks/quicklinks.json

@@ -29,6 +29,7 @@
         "name": "resourcemanager_ui",
         "label": "Oozie Web UI",
         "requires_user_name": "true",
+        "component_name": "OOZIE_SERVER",
         "url":"%@://%@:%@/oozie?user.name=%@",
         "port":{
           "http_property": "oozie.base.url",

+ 4 - 0
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/YARN/quicklinks-mapred/quicklinks.json

@@ -19,6 +19,7 @@
         "name": "jobhistory_ui",
         "label": "JobHistory UI",
         "requires_user_name": "false",
+        "component_name": "HISTORYSERVER",
         "url": "%@://%@:%@",
         "port":{
           "http_property": "mapreduce.jobhistory.webapp.address",
@@ -33,6 +34,7 @@
         "name": "jobhistory_logs",
         "label": "JobHistory logs",
         "requires_user_name": "false",
+        "component_name": "HISTORYSERVER",
         "url": "%@://%@:%@/logs",
         "port":{
           "http_property": "mapreduce.jobhistory.webapp.address",
@@ -47,6 +49,7 @@
         "name":"jobhistory_jmx",
         "label":"JobHistory JMX",
         "requires_user_name":"false",
+        "component_name": "HISTORYSERVER",
         "url":"%@://%@:%@/jmx",
         "port":{
           "http_property": "mapreduce.jobhistory.webapp.address",
@@ -61,6 +64,7 @@
         "name":"thread_stacks",
         "label":"Thread Stacks",
         "requires_user_name": "false",
+        "component_name": "HISTORYSERVER",
         "url":"%@://%@:%@/stacks",
         "port":{
           "http_property": "mapreduce.jobhistory.webapp.address",

+ 4 - 0
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/YARN/quicklinks/quicklinks.json

@@ -19,6 +19,7 @@
         "name": "resourcemanager_ui",
         "label": "ResourceManager UI",
         "requires_user_name": "false",
+        "component_name": "RESOURCEMANAGER",
         "url": "%@://%@:%@",
         "port":{
           "http_property": "yarn.resourcemanager.webapp.address",
@@ -33,6 +34,7 @@
         "name": "resourcemanager_logs",
         "label": "ResourceManager logs",
         "requires_user_name": "false",
+        "component_name": "RESOURCEMANAGER",
         "url": "%@://%@:%@/logs",
         "port":{
           "http_property": "yarn.timeline-service.webapp.address",
@@ -47,6 +49,7 @@
         "name": "resourcemanager_jmx",
         "label":"ResourceManager JMX",
         "requires_user_name": "false",
+        "component_name": "RESOURCEMANAGER",
         "url":"%@://%@:%@/jmx",
         "port":{
           "http_property": "yarn.timeline-service.webapp.address",
@@ -61,6 +64,7 @@
         "name": "thread_stacks",
         "label":"Thread Stacks",
         "requires_user_name": "false",
+        "component_name": "RESOURCEMANAGER",
         "url":"%@://%@:%@/stacks",
         "port":{
           "http_property": "yarn.timeline-service.webapp.address",

+ 1 - 0
ambari-server/src/main/resources/stacks/HDP/2.2/services/RANGER/quicklinks/quicklinks.json

@@ -19,6 +19,7 @@
         "name": "ranger_admin_ui",
         "label": "Ranger Admin UI",
         "requires_user_name": "false",
+        "component_name": "RANGER_ADMIN",
         "url": "%@://%@:%@",
         "port":{
           "http_property": "http.service.port",

+ 1 - 0
ambari-server/src/main/resources/stacks/HDP/2.3/services/ACCUMULO/quicklinks/quicklinks.json

@@ -24,6 +24,7 @@
         "name": "accumulo_monitor_ui",
         "label": "Accumulo Monitor UI",
         "requires_user_name": "false",
+        "component_name": "ACCUMULO_MONITOR",
         "url": "%@://%@:%@/",
         "port":{
           "http_property": "monitor.port.client",

+ 1 - 0
ambari-server/src/main/resources/stacks/HDP/2.3/services/ATLAS/quicklinks/quicklinks.json

@@ -19,6 +19,7 @@
         "name": "atlas_dashboard",
         "label": "Atlas Dashboard",
         "requires_user_name": "true",
+        "component_name": "ATLAS_SERVER",
         "url": "%@://%@:%@/#!/search?user.name=%@",
         "port":{
           "http_property": "atlas.server.http.port",

+ 26 - 0
ambari-server/src/main/resources/stacks/HDP/2.5/services/HIVE/quicklinks/quicklinks.json

@@ -7,10 +7,36 @@
     },
 
     "links": [
+      {
+        "name": "metrics_ui_server",
+        "label": "Hive Dashboard (Grafana)",
+        "requires_user_name": "false",
+        "component_name": "METRICS_GRAFANA",
+        "url":"%@://%@:%@",
+        "port": {
+          "http_property": "port",
+          "http_default_port": "3000",
+          "https_property": "port",
+          "https_default_port": "3000",
+          "regex": "^(\\d+)$",
+          "site": "ams-grafana-ini"
+        },
+        "protocol":{
+          "type":"https",
+          "checks":[
+            {
+              "property":"protocol",
+              "desired":"https",
+              "site":"ams-grafana-ini"
+            }
+          ]
+        }
+      },
       {
         "name": "hiveserver2_ui",
         "label": "HiveServer2 Interactive UI",
         "requires_user_name": "false",
+        "component_name": "HIVE_SERVER_INTERACTIVE",
         "url": "%@://%@:%@/llap/llap0",
         "port":{
           "http_property": "hive.server2.webui.port",

+ 10 - 1
ambari-web/app/controllers/wizard/step6_controller.js

@@ -586,6 +586,15 @@ App.WizardStep6Controller = Em.Controller.extend(App.BlueprintMixin, {
     var hostNames = self.get('hosts').mapProperty('hostName');
     var slaveBlueprint = self.getCurrentBlueprint();
     var masterBlueprint = null;
+    //Existing Installed but invisible masters on `Assign Masters page` should be included in host component layout for recommnedation/validation call
+    var invisibleInstalledMasters = [];
+    if (this.get('isAddServiceWizard')) {
+      var invisibleMasters = App.StackServiceComponent.find().filterProperty("isMaster").filterProperty("isShownOnAddServiceAssignMasterPage", false);
+      invisibleInstalledMasters = invisibleMasters.filter(function(item){
+        var masterComponent = App.MasterComponent.find().findProperty('componentName', item.get('componentName'));
+        return masterComponent && !!masterComponent.get('totalCount');
+      }).mapProperty("componentName");
+    }
     var invisibleSlavesAndClients = App.StackServiceComponent.find().filter(function (component) {
       return component.get("isSlave") && component.get("isShownOnInstallerSlaveClientPage") === false ||
         component.get("isClient") && component.get("isRequiredOnAllHosts");
@@ -597,7 +606,7 @@ App.WizardStep6Controller = Em.Controller.extend(App.BlueprintMixin, {
         return selectedClientComponents.contains(c);
       });
 
-      var invisibleComponents = invisibleSlavesAndClients.concat(alreadyInstalledClients);
+      var invisibleComponents = invisibleInstalledMasters.concat(invisibleSlavesAndClients).concat(alreadyInstalledClients);
 
       var invisibleBlueprint = blueprintUtils.filterByComponents(this.get('content.recommendations'), invisibleComponents);
       masterBlueprint = blueprintUtils.mergeBlueprints(masterBlueprint, invisibleBlueprint);

+ 2 - 2
ambari-web/app/controllers/wizard/step7_controller.js

@@ -545,8 +545,8 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E
         if (isServiceInstalled) {
           var serviceConfigs = stepConfigs.findProperty('serviceName', config.serviceName).get('configs');
           var serviceConfig =  serviceConfigs.filterProperty('filename', fileName).findProperty('name', configName);
-          serviceConfig.set('isEditable', false);
-          config.isEditable = false;
+          serviceConfig.set('isReconfigurable', false);
+          config.isReconfigurable = false;
         }
       }
     }, this);

+ 1 - 19
ambari-web/app/models/quicklinks/quick_links_config.js

@@ -32,22 +32,4 @@ App.QuickLinksConfig = DS.Model.extend({
   links: DS.attr('array')
 });
 
-App.QuickLinksConfig.FIXTURES = [];
-
-/**
- *  key = service name and value = master component name
- * @type {{OOZIE: string, HDFS: string, HBASE: string, YARN: string, HIVE: string, STORM: string, ACCUMULO: string, ATLAS: string, MAPREDUCE2: string, AMBARI_METRICS: string, LOGSEARCH: string}}
- */
-App.QuickLinksConfig.ServiceComponentMap = {
-  'OOZIE': 'OOZIE_SERVER',
-  'HDFS': 'NAMENODE',
-  'HBASE':'HBASE_MASTER',
-  'YARN' : 'RESOURCEMANAGER',
-  'HIVE' : 'HIVE_SERVER_INTERACTIVE',
-  'STORM': 'STORM_UI_SERVER',
-  'ACCUMULO': 'ACCUMULO_MONITOR',
-  'ATLAS': 'ATLAS_SERVER',
-  'MAPREDUCE2': 'HISTORYSERVER',
-  'AMBARI_METRICS': 'METRICS_GRAFANA',
-  'LOGSEARCH': 'LOGSEARCH_SERVER'
-};
+App.QuickLinksConfig.FIXTURES = [];

+ 63 - 44
ambari-web/app/views/common/quick_view_link_view.js

@@ -22,6 +22,7 @@ var App = require('app');
  * @typedef {object} hostForQuickLink
  * @property {string} hostName
  * @property {string} publicHostName
+ * @property {string} componentName
  * @property {?string} status
  */
 
@@ -68,7 +69,7 @@ App.QuickViewLinks = Em.View.extend({
 
   /**
    * services that supports security. this array is used to find out protocol.
-   * besides GANGLIA, YARN, MAPREDUCE2, ACCUMULO. These services use
+   * besides YARN, MAPREDUCE2, ACCUMULO. These services use
    * their properties to know protocol
    */
   servicesSupportsHttps: ["HDFS", "HBASE"],
@@ -245,20 +246,24 @@ App.QuickViewLinks = Em.View.extend({
     var serviceName = this.get('content.serviceName');
     var hosts = this.getHosts(response, serviceName);
     var hasQuickLinks = this.hasQuickLinksConfig(serviceName, hosts);
-    var componentName = App.QuickLinksConfig.ServiceComponentMap[serviceName];
-    var masterComponent = App.MasterComponent.find().findProperty('componentName', componentName);
     var hasHosts = false;
-    if (masterComponent) {
-      hasHosts = !!masterComponent.get('totalCount');
-    }
+    var componentNames = hosts.mapProperty('componentName');
+    componentNames.forEach(function(_componentName){
+      var masterComponent = App.MasterComponent.find().findProperty('componentName', _componentName);
+      if (masterComponent) {
+        hasHosts = hasHosts || !!masterComponent.get('totalCount');
+      }
+    });
     // no need to set quicklinks if
     // 1)current service does not have quick links configured
-    // 2)No host component present for the configured quicklink
+    // 2)No host component present for the configured quicklinks
     this.set('showQuickLinks', hasQuickLinks && hasHosts);
 
+    var isMultipleComponentsInLinks = componentNames.uniq().length > 1;
+
     if (hosts.length === 0) {
       this.setEmptyLinks();
-    } else if (hosts.length === 1) {
+    } else if (hosts.length === 1 || isMultipleComponentsInLinks) {
       this.setSingleHostLinks(hosts, response);
     } else {
       this.setMultipleHostLinks(hosts);
@@ -392,14 +397,21 @@ App.QuickViewLinks = Em.View.extend({
     if (!Em.isNone(quickLinksConfig)) {
       var quickLinks = [];
       var configProperties = this.get('configProperties');
-      var protocol = this.setProtocol(configProperties, quickLinksConfig);
-      var publicHostName = hosts[0].publicHostName;
+      var protocol = this.setProtocol(configProperties, quickLinksConfig.get('protocol'));
 
       var links = Em.get(quickLinksConfig, 'links');
       links.forEach(function (link) {
-        var newItem = this.getHostLink(link, publicHostName, protocol, configProperties, response); //quicklink generated for the hbs template
-        if (!Em.isNone(newItem)) {
-          quickLinks.push(newItem);
+        var componentName = link.component_name;
+        var hostNameForComponent = hosts.findProperty('componentName',componentName);
+        if (hostNameForComponent) {
+          var publicHostName = hostNameForComponent.publicHostName;
+          if (link.protocol) {
+            protocol = this.setProtocol(configProperties, link.protocol);
+          }
+          var newItem = this.getHostLink(link, publicHostName, protocol, configProperties, response); //quicklink generated for the hbs template
+          if (!Em.isNone(newItem)) {
+            quickLinks.push(newItem);
+          }
         }
       }, this);
       this.set('quickLinks', quickLinks);
@@ -431,7 +443,7 @@ App.QuickViewLinks = Em.View.extend({
       var quickLinks = [];
       var configProperties = this.get('configProperties');
 
-      var protocol = this.setProtocol(configProperties, quickLinksConfig);
+      var protocol = this.setProtocol(configProperties, quickLinksConfig.get('protocol'));
       var serviceName = Em.get(quickLinksConfig, 'serviceName');
       var links = Em.get(quickLinksConfig, 'links');
       links.forEach(function (link) {
@@ -594,28 +606,33 @@ App.QuickViewLinks = Em.View.extend({
         publicHostName: App.get('singleNodeAlias')
       }];
     }
-    if (Em.isNone(this.get('content.hostComponents'))) {
-      return [];
-    }
-    switch (serviceName) {
-      case 'OOZIE':
-        return this.processOozieHosts(this.findHosts('OOZIE_SERVER', response));
-      case "HDFS":
-        return this.processHdfsHosts(this.findHosts('NAMENODE', response));
-      case "HBASE":
-        return this.processHbaseHosts(this.findHosts('HBASE_MASTER', response), response);
-      case "YARN":
-        return this.processYarnHosts(this.findHosts('RESOURCEMANAGER', response));
-      default:
-        var componentName = App.QuickLinksConfig.ServiceComponentMap[serviceName];
-        if (componentName) {
-          return this.findHosts(componentName, response);
-        }
-        if (this.getWithDefault('content.hostComponents', []).someProperty('isMaster')) {
-          return this.findHosts(this.get('content.hostComponents').findProperty('isMaster').get('componentName'), response);
+    var hosts = [];
+    var quickLinkConfigs = App.QuickLinksConfig.find().findProperty("id", serviceName);
+    if (quickLinkConfigs) {
+      var links = quickLinkConfigs.get('links');
+      var componentNames = links.mapProperty('component_name').uniq();
+      componentNames.forEach(function (_componentName) {
+        var componentHosts = this.findHosts(_componentName, response);
+        switch (serviceName) {
+          case 'OOZIE':
+            hosts = hosts.concat(this.processOozieHosts(componentHosts));
+            break;
+          case "HDFS":
+            hosts = hosts.concat(this.processHdfsHosts(componentHosts));
+            break;
+          case "HBASE":
+            hosts = hosts.concat(this.processHbaseHosts(componentHosts, response));
+            break;
+          case "YARN":
+            hosts = hosts.concat(this.processYarnHosts(componentHosts));
+            break;
+          default:
+            hosts = hosts.concat(componentHosts);
+            break;
         }
+      }, this);
     }
-    return [];
+    return hosts;
   },
 
   /**
@@ -627,17 +644,20 @@ App.QuickViewLinks = Em.View.extend({
    */
   findHosts: function (componentName, response) {
     var hosts = [];
-    this.get('content.hostComponents')
-      .filterProperty('componentName', componentName)
-      .forEach(function (component) {
-        var host = this.getPublicHostName(response.items, component.get('hostName'));
+    var masterComponent = App.MasterComponent.find().findProperty('componentName', componentName);
+    if (masterComponent) {
+      var masterHostComponents = masterComponent.get('hostNames') || [];
+      masterHostComponents.forEach(function (_hostName) {
+        var host = this.getPublicHostName(response.items, _hostName);
         if (host) {
           hosts.push({
-            hostName: component.get('hostName'),
-            publicHostName: host
+            hostName: _hostName,
+            publicHostName: host,
+            componentName: componentName
           });
         }
       }, this);
+    }
     return hosts;
   },
 
@@ -689,11 +709,11 @@ App.QuickViewLinks = Em.View.extend({
    * protocol becomes "https" otherwise "http" (by default)
    *
    * @param {Object} configProperties
-   * @param {Object} item
+   * @param {Object} protocolConfig
    * @returns {string} "https" or "http" only!
    * @method setProtocol
    */
-  setProtocol: function (configProperties, item) {
+  setProtocol: function (configProperties, protocolConfig) {
     var hadoopSslEnabled = false;
 
     if (!Em.isEmpty(configProperties)) {
@@ -701,7 +721,6 @@ App.QuickViewLinks = Em.View.extend({
       hadoopSslEnabled = hdfsSite && Em.get(hdfsSite, 'properties') && hdfsSite.properties['dfs.http.policy'] === 'HTTPS_ONLY';
     }
 
-    var protocolConfig = Em.get(item, 'protocol');
     if (!protocolConfig) {
       return hadoopSslEnabled ? 'https' : 'http';
     }
@@ -723,7 +742,7 @@ App.QuickViewLinks = Em.View.extend({
       var configType = Em.get(check, 'site');
       var property = Em.get(check, 'property');
       var desiredState = Em.get(check, 'desired');
-      var checkMeet = this.meetDesired(configProperties, configType, property, desiredState)
+      var checkMeet = this.meetDesired(configProperties, configType, property, desiredState);
       if (!checkMeet) {
         count++;
       }

+ 173 - 84
ambari-web/test/views/common/quick_link_view_test.js

@@ -491,19 +491,30 @@ describe('App.QuickViewLinks', function () {
   describe("#findHosts()", function () {
     beforeEach(function () {
       sinon.stub(quickViewLinks, 'getPublicHostName').returns('public_name');
+      sinon.stub(App.MasterComponent, 'find').returns([
+        Em.Object.create({
+          componentName: "C1",
+          hostNames: ["host1", "host2"]
+        })
+      ]);
     });
     afterEach(function () {
       quickViewLinks.getPublicHostName.restore();
+      App.MasterComponent.find.restore();
     });
     it("public_name from getPublicHostName", function () {
-      quickViewLinks.set('content.hostComponents', [Em.Object.create({
-        componentName: 'C1',
-        hostName: 'host1'
-      })]);
-      expect(quickViewLinks.findHosts('C1', {})).to.eql([{
-        hostName: 'host1',
-        publicHostName: 'public_name'
-      }]);
+      expect(quickViewLinks.findHosts('C1', {})).to.eql([
+        {
+          hostName: 'host1',
+          publicHostName: 'public_name',
+          componentName: 'C1'
+        },
+        {
+          hostName: 'host2',
+          publicHostName: 'public_name',
+          componentName: 'C1'
+        }
+      ]);
     });
   });
 
@@ -748,7 +759,7 @@ describe('App.QuickViewLinks', function () {
             {
               type: 'ranger-admin-site',
               properties: {'ranger.service.http.enabled': 'false', 'ranger.service.https.attrib.ssl.enabled': 'true'}
-            },
+            }
           ],
         quickLinksConfig: {
           protocol:{
@@ -779,7 +790,7 @@ describe('App.QuickViewLinks', function () {
             {
               type: 'ranger-admin-site',
               properties: {'ranger.service.http.enabled': 'true', 'ranger.service.https.attrib.ssl.enabled': 'false'}
-            },
+            }
           ],
         quickLinksConfig: {
           protocol:{
@@ -807,7 +818,7 @@ describe('App.QuickViewLinks', function () {
     tests.forEach(function (t) {
       it(t.m, function () {
         quickViewLinks.set('servicesSupportsHttps', t.servicesSupportsHttps);
-        expect(quickViewLinks.setProtocol(t.configProperties, t.quickLinksConfig)).to.equal(t.result);
+        expect(quickViewLinks.setProtocol(t.configProperties, t.quickLinksConfig.protocol)).to.equal(t.result);
       });
     });
   });
@@ -829,7 +840,7 @@ describe('App.QuickViewLinks', function () {
             {
               'type': 'yarn-site',
               'properties': {'yarn.timeline-service.webapp.address': 'c6401.ambari.apache.org:8188'}
-            },
+            }
           ],
         'result': '8188'
       }),
@@ -876,9 +887,99 @@ describe('App.QuickViewLinks', function () {
       sinon.stub(quickViewLinks, 'processYarnHosts').returns(['yarnHost']);
       sinon.stub(quickViewLinks, 'findHosts').returns(['host1']);
       App.set('singleNodeInstall', false);
-      quickViewLinks.set('content', Em.Object.create({
-        hostComponents: []
-      }));
+      sinon.stub(App.QuickLinksConfig, 'find').returns([
+        Em.Object.create({
+          id: 'OOZIE',
+          links: [
+            {
+              component_name: 'OOZIE_SERVER'
+            }
+          ]
+        }),
+        Em.Object.create({
+          id: 'HDFS',
+          links: [
+            {
+              component_name: 'NAMENODE'
+            }
+          ]
+        }),
+        Em.Object.create({
+          id: 'HBASE',
+          links: [
+            {
+              component_name: 'HBASE_MASTER'
+            }
+          ]
+        }),
+        Em.Object.create({
+          id: 'YARN',
+          links: [
+            {
+              component_name: 'RESOURCEMANAGER'
+            }
+          ]
+        }),
+        Em.Object.create({
+          id: 'STORM',
+          links: [
+            {
+              component_name: 'STORM_UI_SERVER'
+            }
+          ]
+        }),
+        Em.Object.create({
+          id: 'ACCUMULO',
+          links: [
+            {
+              component_name: 'ACCUMULO_MONITOR'
+            }
+          ]
+        }),
+        Em.Object.create({
+          id: 'ATLAS',
+          links: [
+            {
+              component_name: 'ATLAS_SERVER'
+            }
+          ]
+        }),
+        Em.Object.create({
+          id: 'MAPREDUCE2',
+          links: [
+            {
+              component_name: 'HISTORYSERVER'
+            }
+          ]
+        }),
+        Em.Object.create({
+          id: 'AMBARI_METRICS',
+          links: [
+            {
+              component_name: 'METRICS_GRAFANA'
+            }
+          ]
+        }),
+        Em.Object.create({
+          id: 'LOGSEARCH',
+          links: [
+            {
+              component_name: 'LOGSEARCH_SERVER'
+            }
+          ]
+        }),
+        Em.Object.create({
+          id: 'HIVE',
+          links: [
+            {
+              component_name: 'METRICS_GRAFANA'
+            },
+            {
+              component_name: 'HIVE_SERVER_INTERACTIVE'
+            }
+          ]
+        })
+      ]);
     });
     afterEach(function() {
       quickViewLinks.processOozieHosts.restore();
@@ -886,6 +987,7 @@ describe('App.QuickViewLinks', function () {
       quickViewLinks.processHbaseHosts.restore();
       quickViewLinks.findHosts.restore();
       quickViewLinks.processYarnHosts.restore();
+      App.QuickLinksConfig.find.restore();
     });
 
     it("singleNodeInstall is true", function() {
@@ -897,81 +999,68 @@ describe('App.QuickViewLinks', function () {
       }])
     });
 
-    it("content is null", function() {
-      quickViewLinks.set('content', null);
-      expect(quickViewLinks.getHosts({}, 'S1')).to.be.empty;
-    });
-
-    it("OOZIE service", function() {
-      expect(quickViewLinks.getHosts({}, 'OOZIE')).to.eql(['oozieHost']);
-      expect(quickViewLinks.findHosts.calledWith('OOZIE_SERVER', {})).to.be.true;
-      expect(quickViewLinks.processOozieHosts.calledOnce).to.be.true;
-    });
-
-    it("HDFS service", function() {
-      expect(quickViewLinks.getHosts({}, 'HDFS')).to.eql(['hdfsHost']);
-      expect(quickViewLinks.findHosts.calledWith('NAMENODE', {})).to.be.true;
-      expect(quickViewLinks.processHdfsHosts.calledOnce).to.be.true;
-    });
-
-    it("HBASE service", function() {
-      expect(quickViewLinks.getHosts({}, 'HBASE')).to.eql(['hbaseHost']);
-      expect(quickViewLinks.findHosts.calledWith('HBASE_MASTER', {})).to.be.true;
-      expect(quickViewLinks.processHbaseHosts.calledOnce).to.be.true;
-    });
-
-    it("YARN service", function() {
-      expect(quickViewLinks.getHosts({}, 'YARN')).to.eql(['yarnHost']);
-      expect(quickViewLinks.findHosts.calledWith('RESOURCEMANAGER', {})).to.be.true;
-      expect(quickViewLinks.processYarnHosts.calledOnce).to.be.true;
-    });
-
-    it("STORM service", function() {
-      expect(quickViewLinks.getHosts({}, 'STORM')).to.eql(['host1']);
-      expect(quickViewLinks.findHosts.calledWith('STORM_UI_SERVER', {})).to.be.true;
-    });
-
-    it("ACCUMULO service", function() {
-      expect(quickViewLinks.getHosts({}, 'ACCUMULO')).to.eql(['host1']);
-      expect(quickViewLinks.findHosts.calledWith('ACCUMULO_MONITOR', {})).to.be.true;
-    });
-
-    it("ATLAS service", function() {
-      expect(quickViewLinks.getHosts({}, 'ATLAS')).to.eql(['host1']);
-      expect(quickViewLinks.findHosts.calledWith('ATLAS_SERVER', {})).to.be.true;
-    });
-
-    it("MAPREDUCE2 service", function() {
-      expect(quickViewLinks.getHosts({}, 'MAPREDUCE2')).to.eql(['host1']);
-      expect(quickViewLinks.findHosts.calledWith('HISTORYSERVER', {})).to.be.true;
-    });
-
-    it("AMBARI_METRICS service", function() {
-      expect(quickViewLinks.getHosts({}, 'AMBARI_METRICS')).to.eql(['host1']);
-      expect(quickViewLinks.findHosts.calledWith('METRICS_GRAFANA', {})).to.be.true;
-    });
+    var tests = [
+      {
+        serviceName: 'OOZIE',
+        callback: 'processOozieHosts',
+        result: ['oozieHost']
+      },
+      {
+        serviceName: 'HDFS',
+        callback: 'processHdfsHosts',
+        result: ['hdfsHost']
+      },
+      {
+        serviceName: 'HBASE',
+        callback: 'processHbaseHosts',
+        result: ['hbaseHost']
+      },
+      {
+        serviceName: 'YARN',
+        callback: 'processYarnHosts',
+        result: ['yarnHost']
+      },
+      {
+        serviceName: 'STORM'
+      },
+      {
+        serviceName: 'ACCUMULO'
+      },
+      {
+        serviceName: 'ATLAS'
+      },
+      {
+        serviceName: 'MAPREDUCE2'
+      },
+      {
+        serviceName: 'AMBARI_METRICS'
+      },
+      {
+        serviceName: 'LOGSEARCH'
+      },
+      {
+        serviceName: 'HIVE',
+        result: ['host1', 'host1']
+      }
+    ];
 
-    it("LOGSEARCH service", function() {
-      expect(quickViewLinks.getHosts({}, 'LOGSEARCH')).to.eql(['host1']);
-      expect(quickViewLinks.findHosts.calledWith('LOGSEARCH_SERVER', {})).to.be.true;
+    tests.forEach(function(_test){
+      var serviceName =_test.serviceName;
+      it(serviceName, function() {
+        var componentNames = App.QuickLinksConfig.find().findProperty('id', serviceName).get('links').mapProperty('component_name');
+        expect(quickViewLinks.getHosts({}, serviceName)).to.eql(_test.result || ['host1']);
+        componentNames.forEach(function(_componentName){
+          expect(quickViewLinks.findHosts.calledWith(_componentName, {})).to.be.true;
+        });
+        if (_test.callback) {
+          expect(quickViewLinks[_test.callback].calledOnce).to.be.true;
+        }
+      });
     });
 
     it("custom service without master", function() {
       expect(quickViewLinks.getHosts({}, 'S1')).to.be.empty;
     });
-
-    it("custom service with master", function() {
-      quickViewLinks.set('content', Em.Object.create({
-        hostComponents: [
-          Em.Object.create({
-            isMaster: true,
-            componentName: 'C1'
-          })
-        ]
-      }));
-      expect(quickViewLinks.getHosts({}, 'S1')).to.eql(['host1']);
-      expect(quickViewLinks.findHosts.calledWith('C1', {})).to.be.true;
-    });
   });
 
   describe('#reverseType', function () {