Browse Source

AMBARI-11268: Making the quick links stack driven with quicklinks.json file in each servcie's stack file directory (dili)

Di Li 9 years ago
parent
commit
eca979132c
60 changed files with 3122 additions and 297 deletions
  1. 4 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
  2. 1 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/StackServiceResourceDefinition.java
  3. 1 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java
  4. 36 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/StacksService.java
  5. 2 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
  6. 201 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java
  7. 2 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
  8. 139 0
      ambari-server/src/main/java/org/apache/ambari/server/stack/QuickLinksConfigurationModule.java
  9. 42 2
      ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
  10. 87 0
      ambari-server/src/main/java/org/apache/ambari/server/state/QuickLinksConfigurationInfo.java
  11. 48 0
      ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java
  12. 60 0
      ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Check.java
  13. 127 0
      ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Link.java
  14. 116 0
      ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Port.java
  15. 51 0
      ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Protocol.java
  16. 78 0
      ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/QuickLinks.java
  17. 91 0
      ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/QuickLinksConfiguration.java
  18. 6 0
      ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/metainfo.xml
  19. 28 0
      ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/quicklinks/quicklinks.json
  20. 6 0
      ambari-server/src/main/resources/common-services/STORM/0.9.1.2.1/metainfo.xml
  21. 28 0
      ambari-server/src/main/resources/common-services/STORM/0.9.1.2.1/quicklinks/quicklinks.json
  22. 7 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/metainfo.xml
  23. 45 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/quicklinks/quicklinks.json
  24. 6 0
      ambari-server/src/main/resources/stacks/HDP/2.2/services/RANGER/metainfo.xml
  25. 35 0
      ambari-server/src/main/resources/stacks/HDP/2.2/services/RANGER/quicklinks/quicklinks.json
  26. 6 0
      ambari-server/src/main/resources/stacks/HDP/2.2/services/SPARK/metainfo.xml
  27. 28 0
      ambari-server/src/main/resources/stacks/HDP/2.2/services/SPARK/quicklinks/quicklinks.json
  28. 6 1
      ambari-server/src/main/resources/stacks/HDP/2.3/services/ACCUMULO/metainfo.xml
  29. 40 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/ACCUMULO/quicklinks/quicklinks.json
  30. 6 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/ATLAS/metainfo.xml
  31. 35 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/ATLAS/quicklinks/quicklinks.json
  32. 6 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/HBASE/metainfo.xml
  33. 103 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/HBASE/quicklinks/quicklinks.json
  34. 6 1
      ambari-server/src/main/resources/stacks/HDP/2.3/services/HDFS/metainfo.xml
  35. 80 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/HDFS/quicklinks/quicklinks.json
  36. 6 1
      ambari-server/src/main/resources/stacks/HDP/2.3/services/OOZIE/metainfo.xml
  37. 45 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/OOZIE/quicklinks/quicklinks.json
  38. 6 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/RANGER/metainfo.xml
  39. 40 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/RANGER/quicklinks/quicklinks.json
  40. 6 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/SPARK/metainfo.xml
  41. 28 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/SPARK/quicklinks/quicklinks.json
  42. 13 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/YARN/metainfo.xml
  43. 80 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/YARN/quicklinks-mapred/quicklinks.json
  44. 80 0
      ambari-server/src/main/resources/stacks/HDP/2.3/services/YARN/quicklinks/quicklinks.json
  45. 1 1
      ambari-server/src/test/java/org/apache/ambari/server/api/resources/StackServiceResourceDefinitionTest.java
  46. 128 0
      ambari-server/src/test/java/org/apache/ambari/server/stack/QuickLinksConfigurationModuleTest.java
  47. 7 0
      ambari-server/src/test/resources/child_quicklinks_to_inherit.json
  48. 65 0
      ambari-server/src/test/resources/child_quicklinks_to_merge.json
  49. 90 0
      ambari-server/src/test/resources/child_quicklinks_to_override.json
  50. 80 0
      ambari-server/src/test/resources/parent_quicklinks.json
  51. 102 0
      ambari-web/app/assets/data/configurations/quicklinks.json
  52. 112 0
      ambari-web/app/assets/data/configurations/quicklinks_services.json
  53. 1 0
      ambari-web/app/mappers.js
  54. 50 0
      ambari-web/app/mappers/quicklinks_mapper.js
  55. 1 0
      ambari-web/app/models.js
  56. 35 0
      ambari-web/app/models/quicklinks/quick_links_config.js
  57. 4 4
      ambari-web/app/templates/main/service/item.hbs
  58. 12 0
      ambari-web/app/utils/ajax/ajax.js
  59. 254 164
      ambari-web/app/views/common/quick_view_link_view.js
  60. 313 123
      ambari-web/test/views/common/quick_link_view_test.js

+ 4 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java

@@ -372,6 +372,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
         resourceDefinition = new SimpleResourceDefinition(Resource.Type.Theme, "theme", "themes");
         break;
 
+      case QuickLink:
+        resourceDefinition = new SimpleResourceDefinition(Resource.Type.QuickLink, "quicklink", "quicklinks");
+        break;
+
       case Widget:
         resourceDefinition = new WidgetResourceDefinition();
         break;

+ 1 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/StackServiceResourceDefinition.java

@@ -52,6 +52,7 @@ public class StackServiceResourceDefinition extends BaseResourceDefinition {
     setChildren.add(new SubResourceDefinition(Resource.Type.StackServiceComponent));
     setChildren.add(new SubResourceDefinition(Type.StackArtifact));
     setChildren.add(new SubResourceDefinition(Resource.Type.Theme));
+    setChildren.add(new SubResourceDefinition(Resource.Type.QuickLink));
 
     return setChildren;
 

+ 1 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java

@@ -93,6 +93,7 @@ import static org.apache.ambari.server.controller.utilities.PropertyHelper.AGGRE
 public class AmbariMetaInfo {
   public static final String SERVICE_CONFIG_FOLDER_NAME = "configuration";
   public static final String SERVICE_THEMES_FOLDER_NAME = "themes";
+  public static final String SERVICE_QUICKLINKS_CONFIGURATIONS_FOLDER_NAME = "quicklinks";
   public static final String SERVICE_CONFIG_FILE_NAME_POSTFIX = ".xml";
   public static final String RCO_FILE_NAME = "role_command_order.json";
   public static final String SERVICE_METRIC_FILE_NAME = "metrics.json";

+ 36 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/StacksService.java

@@ -190,6 +190,31 @@ public class StacksService extends BaseService {
       createStackServiceThemesResource(stackName, stackVersion, serviceName, themeName));
   }
 
+  @GET
+  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/quicklinks")
+  @Produces("text/plain")
+  public Response getStackServiceQuickLinksConfigurations(String body, @Context HttpHeaders headers,
+                                           @Context UriInfo ui, @PathParam("stackName") String stackName,
+                                           @PathParam("stackVersion") String stackVersion,
+                                           @PathParam("serviceName") String serviceName) {
+
+    return handleRequest(headers, body, ui, Request.Type.GET,
+      createStackServiceQuickLinksResource(stackName, stackVersion, serviceName, null));
+  }
+
+  @GET
+  @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/quicklinks/{quickLinksConfigurationName}")
+  @Produces("text/plain")
+  public Response getStackServiceQuickLinksConfiguration(String body, @Context HttpHeaders headers,
+                                           @Context UriInfo ui, @PathParam("stackName") String stackName,
+                                           @PathParam("stackVersion") String stackVersion,
+                                           @PathParam("serviceName") String serviceName,
+                                           @PathParam("quickLinksConfigurationName") String quickLinksConfigurationName) {
+
+    return handleRequest(headers, body, ui, Request.Type.GET,
+      createStackServiceQuickLinksResource(stackName, stackVersion, serviceName, quickLinksConfigurationName));
+  }
+
   @GET
   @Path("{stackName}/versions/{stackVersion}/services/{serviceName}/artifacts/{artifactName}")
   @Produces("text/plain")
@@ -455,6 +480,17 @@ public class StacksService extends BaseService {
     return createResource(Resource.Type.Theme, mapIds);
   }
 
+  ResourceInstance createStackServiceQuickLinksResource(String stackName, String stackVersion, String serviceName,
+      String quickLinksConfigurationName) {
+    Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+    mapIds.put(Resource.Type.Stack, stackName);
+    mapIds.put(Resource.Type.StackVersion, stackVersion);
+    mapIds.put(Resource.Type.StackService, serviceName);
+    mapIds.put(Resource.Type.QuickLink, quickLinksConfigurationName);
+
+    return createResource(Resource.Type.QuickLink, mapIds);
+  }
+
   ResourceInstance createStackResource(String stackName) {
 
     return createResource(Resource.Type.Stack,

+ 2 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java

@@ -177,6 +177,8 @@ public abstract class AbstractControllerResourceProvider extends AbstractAuthori
         return new StackArtifactResourceProvider(managementController);
       case Theme:
         return new ThemeArtifactResourceProvider(managementController);
+      case QuickLink:
+        return new QuickLinkArtifactResourceProvider(managementController);
       case ActiveWidgetLayout:
         return new ActiveWidgetLayoutResourceProvider(managementController);
       case WidgetLayout:

+ 201 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProvider.java

@@ -0,0 +1,201 @@
+/*
+ * 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.
+ */
+
+package org.apache.ambari.server.controller.internal;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.NoSuchResourceException;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.state.QuickLinksConfigurationInfo;
+import org.apache.ambari.server.state.ServiceInfo;
+import org.apache.ambari.server.state.StackInfo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class QuickLinkArtifactResourceProvider extends AbstractControllerResourceProvider {
+
+  public static final String STACK_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("QuickLinkInfo", "stack_name");
+  public static final String STACK_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId("QuickLinkInfo", "stack_version");
+  public static final String STACK_SERVICE_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("QuickLinkInfo", "service_name");
+  public static final String QUICKLINK_DEFAULT_PROPERTY_ID = PropertyHelper.getPropertyId("QuickLinkInfo", "default");
+  public static final String QUICKLINK_FILE_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("QuickLinkInfo", "file_name");
+  public static final String QUICKLINK_DATA_PROPERTY_ID = PropertyHelper.getPropertyId("QuickLinkInfo", "quicklink_data");
+
+  /**
+   * primary key fields
+   */
+  public static Set<String> pkPropertyIds = new HashSet<String>();
+  /**
+   * map of resource type to fk field
+   */
+  public static Map<Resource.Type, String> keyPropertyIds =
+    new HashMap<Resource.Type, String>();
+
+  /**
+   * resource properties
+   */
+  public static Set<String> propertyIds = new HashSet<String>();
+
+  static {
+    keyPropertyIds.put(Resource.Type.QuickLink, QUICKLINK_FILE_NAME_PROPERTY_ID);
+    keyPropertyIds.put(Resource.Type.Stack, STACK_NAME_PROPERTY_ID);
+    keyPropertyIds.put(Resource.Type.StackVersion, STACK_VERSION_PROPERTY_ID);
+    keyPropertyIds.put(Resource.Type.StackService, STACK_SERVICE_NAME_PROPERTY_ID);
+
+    pkPropertyIds.add(QUICKLINK_FILE_NAME_PROPERTY_ID);
+
+    // resource properties
+    propertyIds.add(STACK_NAME_PROPERTY_ID);
+    propertyIds.add(STACK_VERSION_PROPERTY_ID);
+    propertyIds.add(STACK_SERVICE_NAME_PROPERTY_ID);
+    propertyIds.add(QUICKLINK_FILE_NAME_PROPERTY_ID);
+    propertyIds.add(QUICKLINK_DATA_PROPERTY_ID);
+    propertyIds.add(QUICKLINK_DEFAULT_PROPERTY_ID);
+  }
+
+  /**
+   * Create a  new resource provider for the given management controller.
+   *
+   * @param managementController the management controller
+   */
+  protected QuickLinkArtifactResourceProvider(AmbariManagementController managementController) {
+    super(propertyIds, keyPropertyIds, managementController);
+  }
+
+  @Override
+  public RequestStatus createResources(Request request) throws SystemException, UnsupportedPropertyException, ResourceAlreadyExistsException,
+      NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Creating of quick links is not supported");
+  }
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException,
+      NoSuchParentResourceException {
+
+    Set<Resource> resources = new LinkedHashSet<Resource>();
+
+    resources.addAll(getQuickLinks(request, predicate));
+    // add other artifacts types here
+
+    if (resources.isEmpty()) {
+      throw new NoSuchResourceException(
+        "The requested resource doesn't exist: QuickLink not found, " + predicate);
+    }
+
+    return resources;
+
+  }
+
+  @Override
+  public RequestStatus updateResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException,
+      NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Updating of quick links is not supported");
+  }
+
+  @Override
+  public RequestStatus deleteResources(Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Deleting of quick links is not supported");
+  }
+
+  private Set<Resource> getQuickLinks(Request request, Predicate predicate) throws NoSuchParentResourceException,
+    NoSuchResourceException, UnsupportedPropertyException, SystemException {
+
+    Set<Resource> resources = new LinkedHashSet<Resource>();
+    for (Map<String, Object> properties : getPropertyMaps(predicate)) {
+      String quickLinksFileName = (String) properties.get(QUICKLINK_FILE_NAME_PROPERTY_ID);
+      String stackName = (String) properties.get(STACK_NAME_PROPERTY_ID);
+      String stackVersion = (String) properties.get(STACK_VERSION_PROPERTY_ID);
+      String stackService = (String) properties.get(STACK_SERVICE_NAME_PROPERTY_ID);
+
+      StackInfo stackInfo;
+      try {
+        stackInfo = getManagementController().getAmbariMetaInfo().getStack(stackName, stackVersion);
+      } catch (AmbariException e) {
+        throw new NoSuchParentResourceException(String.format(
+          "Parent stack resource doesn't exist: stackName='%s', stackVersion='%s'", stackName, stackVersion));
+      }
+
+      List<ServiceInfo> serviceInfoList = new ArrayList<ServiceInfo>();
+
+      if (stackService == null) {
+        serviceInfoList.addAll(stackInfo.getServices());
+      } else {
+        ServiceInfo service = stackInfo.getService(stackService);
+        if (service == null) {
+          throw new NoSuchParentResourceException(String.format(
+            "Parent stack/service resource doesn't exist: stackName='%s', stackVersion='%s', serviceName='%s'",
+            stackName, stackVersion, stackService));
+        }
+        serviceInfoList.add(service);
+      }
+
+      for (ServiceInfo serviceInfo : serviceInfoList) {
+        List<QuickLinksConfigurationInfo> serviceQuickLinks = new ArrayList<QuickLinksConfigurationInfo>();
+        if (quickLinksFileName != null) {
+          LOG.debug("Getting quick links from service {}, quick links = {}", serviceInfo.getName(), serviceInfo.getQuickLinksConfigurationsMap());
+          serviceQuickLinks.add(serviceInfo.getQuickLinksConfigurationsMap().get(quickLinksFileName));
+        } else {
+          for (QuickLinksConfigurationInfo quickLinksConfigurationInfo : serviceInfo.getQuickLinksConfigurationsMap().values()) {
+            if (quickLinksConfigurationInfo.getIsDefault()) {
+              serviceQuickLinks.add(0, quickLinksConfigurationInfo );
+            } else {
+              serviceQuickLinks.add(quickLinksConfigurationInfo );
+            }
+          }
+        }
+
+        List<Resource> serviceResources = new ArrayList<Resource>();
+        for (QuickLinksConfigurationInfo quickLinksConfigurationInfo : serviceQuickLinks) {
+          Resource resource = new ResourceImpl(Resource.Type.QuickLink);
+          Set<String> requestedIds = getRequestPropertyIds(request, predicate);
+          setResourceProperty(resource, QUICKLINK_FILE_NAME_PROPERTY_ID, quickLinksConfigurationInfo.getFileName(), requestedIds);
+          setResourceProperty(resource, QUICKLINK_DATA_PROPERTY_ID, quickLinksConfigurationInfo.getQuickLinksConfigurationMap(), requestedIds);
+          setResourceProperty(resource, QUICKLINK_DEFAULT_PROPERTY_ID, quickLinksConfigurationInfo.getIsDefault(), requestedIds);
+          setResourceProperty(resource, STACK_NAME_PROPERTY_ID, stackName, requestedIds);
+          setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, stackVersion, requestedIds);
+          setResourceProperty(resource, STACK_SERVICE_NAME_PROPERTY_ID, serviceInfo.getName(), requestedIds);
+          serviceResources.add(resource);
+        }
+
+        resources.addAll(serviceResources);
+
+      }
+    }
+    return resources;
+  }
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return null;
+  }
+}

+ 2 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java

@@ -144,6 +144,7 @@ public interface Resource {
     WidgetLayout,
     ActiveWidgetLayout,
     Theme,
+    QuickLink,
     HostKerberosIdentity,
     Credential,
     KerberosDescriptor,
@@ -250,6 +251,7 @@ public interface Resource {
     public static final Type StackArtifact = InternalType.StackArtifact.getType();
     public static final Type Artifact = InternalType.Artifact.getType();
     public static final Type Theme = InternalType.Theme.getType();
+    public static final Type QuickLink = InternalType.QuickLink.getType();
     public static final Type Widget = InternalType.Widget.getType();
     public static final Type WidgetLayout = InternalType.WidgetLayout.getType();
     public static final Type ActiveWidgetLayout = InternalType.ActiveWidgetLayout.getType();

+ 139 - 0
ambari-server/src/main/java/org/apache/ambari/server/stack/QuickLinksConfigurationModule.java

@@ -0,0 +1,139 @@
+/**
+ * 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.
+ */
+package org.apache.ambari.server.stack;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.state.QuickLinksConfigurationInfo;
+import org.apache.ambari.server.state.quicklinks.QuickLinks;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class QuickLinksConfigurationModule extends BaseModule<QuickLinksConfigurationModule, QuickLinksConfigurationInfo> implements Validable {
+
+  private static final Logger LOG = LoggerFactory.getLogger(QuickLinksConfigurationModule.class);
+  private static final ObjectMapper mapper = new ObjectMapper();
+
+  public static final String QUICKLINKS_CONFIGURATION_KEY = "QuickLinksConfiguration";
+
+  static {
+  }
+
+
+  private QuickLinksConfigurationInfo moduleInfo;
+  private boolean valid = true;
+  private Set<String> errors = new HashSet<String>();
+
+  public QuickLinksConfigurationModule(File quickLinksConfigurationFile) {
+    this(quickLinksConfigurationFile, new QuickLinksConfigurationInfo());
+  }
+
+  public QuickLinksConfigurationModule(File quickLinksConfigurationFile, QuickLinksConfigurationInfo moduleInfo) {
+    this.moduleInfo = moduleInfo;
+    if (!moduleInfo.isDeleted() && quickLinksConfigurationFile != null) {
+      LOG.debug("Looking for quicklinks in {}", quickLinksConfigurationFile.getAbsolutePath());
+      FileReader reader = null;
+      try {
+        reader = new FileReader(quickLinksConfigurationFile);
+      } catch (FileNotFoundException e) {
+        LOG.error("Quick links file not found");
+      }
+      try {
+        QuickLinks quickLinksConfig = mapper.readValue(reader, QuickLinks.class);
+        Map<String, QuickLinks> map = new HashMap<String, QuickLinks>();
+        map.put(QUICKLINKS_CONFIGURATION_KEY, quickLinksConfig);
+        moduleInfo.setQuickLinksConfigurationMap(map);
+        LOG.debug("Loaded quicklinks configuration: {}", moduleInfo);
+      } catch (IOException e) {
+        String errorMessage = String.format("Unable to parse quicklinks configuration file %s", quickLinksConfigurationFile.getAbsolutePath());
+        LOG.error(errorMessage,  e);
+        setValid(false);
+        setErrors(errorMessage);
+      }
+    }
+  }
+
+  public QuickLinksConfigurationModule(QuickLinksConfigurationInfo moduleInfo) {
+    this.moduleInfo = moduleInfo;
+  }
+
+  @Override
+  public void resolve(QuickLinksConfigurationModule parent, Map<String, StackModule> allStacks, Map<String, ServiceModule> commonServices) throws AmbariException {
+    QuickLinksConfigurationInfo parentModuleInfo = parent.getModuleInfo();
+
+    if (parent.getModuleInfo() != null && !moduleInfo.isDeleted()) {
+      if (moduleInfo.getQuickLinksConfigurationMap() == null || moduleInfo.getQuickLinksConfigurationMap().isEmpty()) {
+        moduleInfo.setQuickLinksConfigurationMap(parentModuleInfo.getQuickLinksConfigurationMap());
+      } else if(parentModuleInfo.getQuickLinksConfigurationMap() != null && !parentModuleInfo.getQuickLinksConfigurationMap().isEmpty()) {
+        QuickLinks child = moduleInfo.getQuickLinksConfigurationMap().get(QUICKLINKS_CONFIGURATION_KEY);
+        QuickLinks parentConfig= parentModuleInfo.getQuickLinksConfigurationMap().get(QUICKLINKS_CONFIGURATION_KEY);
+        child.mergeWithParent(parentConfig);
+      }
+    }
+  }
+
+  @Override
+  public QuickLinksConfigurationInfo getModuleInfo() {
+    return moduleInfo;
+  }
+
+  @Override
+  public boolean isDeleted() {
+    return false;
+  }
+
+  @Override
+  public String getId() {
+    return moduleInfo.getFileName();
+  }
+
+  @Override
+  public boolean isValid() {
+    return valid;
+  }
+
+  @Override
+  public void setValid(boolean valid) {
+    this.valid = valid;
+  }
+
+  @Override
+  public void setErrors(String error) {
+    errors.add(error);
+  }
+
+  @Override
+  public void setErrors(Collection<String> error) {
+    errors.addAll(error);
+  }
+
+  @Override
+  public Collection<String> getErrors() {
+    return errors;
+  }
+}

+ 42 - 2
ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java

@@ -23,19 +23,20 @@ import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Multimaps;
 import com.google.common.collect.Sets;
+
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.state.ComponentInfo;
 import org.apache.ambari.server.state.CustomCommandDefinition;
 import org.apache.ambari.server.state.PropertyInfo;
+import org.apache.ambari.server.state.QuickLinksConfigurationInfo;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.ServicePropertyInfo;
 import org.apache.ambari.server.state.ThemeInfo;
 
 import javax.annotation.Nullable;
+
 import java.io.File;
 import java.util.Collection;
 import java.util.Collections;
@@ -78,6 +79,11 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
    */
   private Map<String, ThemeModule> themeModules = new HashMap<String, ThemeModule>();
 
+  /**
+   * Map of quicklinks, single value currently
+   */
+  private Map<String, QuickLinksConfigurationModule> quickLinksConfigurationModules = new HashMap<String, QuickLinksConfigurationModule>();
+
   /**
    * Encapsulates IO operations on service directory
    */
@@ -129,6 +135,7 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
     populateComponentModules();
     populateConfigurationModules();
     populateThemeModules();
+    populateQuickLinksConfigurationModules();
 
     validateServiceInfo();
   }
@@ -204,6 +211,7 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
     mergeComponents(parentModule, allStacks, commonServices);
     mergeConfigurations(parentModule, allStacks, commonServices);
     mergeThemes(parentModule, allStacks, commonServices);
+    mergeQuickLinksConfigurations(parentModule, allStacks, commonServices);
     mergeExcludedConfigTypes(parent);
 
 
@@ -384,9 +392,41 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
       } else {
         serviceInfo.getThemesMap().remove(moduleInfo.getFileName());
       }
+    }
+  }
 
+  private void populateQuickLinksConfigurationModules(){
+    if (serviceInfo.getQuickLinksConfigurationsDir() == null) {
+      serviceInfo.setQuickLinksConfigurationsDir(AmbariMetaInfo.SERVICE_QUICKLINKS_CONFIGURATIONS_FOLDER_NAME);
     }
 
+    String quickLinksConfigurationsDir = serviceDirectory.getAbsolutePath() + File.separator + serviceInfo.getQuickLinksConfigurationsDir();
+
+    if (serviceInfo.getQuickLinksConfigurations() != null) {
+      for (QuickLinksConfigurationInfo quickLinksConfigurationInfo: serviceInfo.getQuickLinksConfigurations()) {
+        File file = new File(quickLinksConfigurationsDir + File.separator + quickLinksConfigurationInfo.getFileName());
+        QuickLinksConfigurationModule module = new QuickLinksConfigurationModule(file, quickLinksConfigurationInfo);
+        quickLinksConfigurationModules.put(module.getId(), module);
+      }
+    }    //Not fail if quicklinks.json file contains errors
+  }
+
+  /**
+   * Merge theme modules.
+   */
+  private void mergeQuickLinksConfigurations(ServiceModule parent, Map<String, StackModule> allStacks,
+                           Map<String, ServiceModule> commonServices) throws AmbariException {
+    Collection<QuickLinksConfigurationModule> mergedModules = mergeChildModules(allStacks, commonServices, quickLinksConfigurationModules, parent.quickLinksConfigurationModules);
+
+    for (QuickLinksConfigurationModule mergedModule : mergedModules) {
+      quickLinksConfigurationModules.put(mergedModule.getId(), mergedModule);
+      QuickLinksConfigurationInfo moduleInfo = mergedModule.getModuleInfo();
+      if (!moduleInfo.isDeleted()) {
+        serviceInfo.getQuickLinksConfigurationsMap().put(moduleInfo.getFileName(), moduleInfo);
+      } else {
+        serviceInfo.getQuickLinksConfigurationsMap().remove(moduleInfo.getFileName());
+      }
+    }
   }
 
   /**

+ 87 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/QuickLinksConfigurationInfo.java

@@ -0,0 +1,87 @@
+/**
+ * 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.
+ */
+package org.apache.ambari.server.state;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlTransient;
+
+import org.apache.ambari.server.state.quicklinks.QuickLinks;
+
+import java.util.Map;
+
+/**
+ * Wrapper for quickLinksConfiguration description
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+public class QuickLinksConfigurationInfo {
+
+  private String fileName;
+  @XmlElement(name = "default")
+  private Boolean isDefault = false;
+  private Boolean deleted = false;
+
+  @XmlTransient
+  private Map<String, QuickLinks> quickLinksConfigurationMap = null;
+
+  public QuickLinksConfigurationInfo() {
+  }
+
+  public Map<String, QuickLinks> getQuickLinksConfigurationMap() {
+    return quickLinksConfigurationMap;
+  }
+
+  public void setQuickLinksConfigurationMap(Map<String, QuickLinks> quickLinksConfigurationMap) {
+    this.quickLinksConfigurationMap = quickLinksConfigurationMap;
+  }
+
+  @Override
+  public String toString() {
+    return "QuickLinksConfigurationInfo{" +
+      "deleted=" + deleted +
+      ", fileName='" + fileName + '\'' +
+      ", isDefault=" + isDefault +
+      ", quickLinksConfigurationMap=" + quickLinksConfigurationMap +
+      '}';
+  }
+
+  public String getFileName() {
+    return fileName;
+  }
+
+  public void setFileName(String fileName) {
+    this.fileName = fileName;
+  }
+
+  public Boolean getIsDefault() {
+    return isDefault;
+  }
+
+  public void setIsDefault(Boolean isDefault) {
+    this.isDefault = isDefault;
+  }
+
+  public Boolean isDeleted() {
+    return deleted;
+  }
+
+  public void setDeleted(Boolean deleted) {
+    this.deleted = deleted;
+  }
+}

+ 48 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java

@@ -210,6 +210,17 @@ public class ServiceInfo implements Validable{
   @XmlTransient
   private volatile Map<String, ThemeInfo> themesMap;
 
+  @JsonIgnore
+  @XmlElement(name = "quickLinksConfigurations-dir")
+  private String quickLinksConfigurationsDir = AmbariMetaInfo.SERVICE_QUICKLINKS_CONFIGURATIONS_FOLDER_NAME;
+
+  @JsonIgnore
+  @XmlElementWrapper(name = "quickLinksConfigurations")
+  @XmlElements(@XmlElement(name = "quickLinksConfiguration"))
+  private List<QuickLinksConfigurationInfo> quickLinksConfigurations;
+
+  @XmlTransient
+  private volatile Map<String, QuickLinksConfigurationInfo> quickLinksConfigurationsMap;
 
   /**
    * Map of of os-specific details that is exposed (and initialised from list)
@@ -769,6 +780,43 @@ public String getVersion() {
     this.themesMap = themesMap;
   }
 
+  //Quick links configurations
+  public String getQuickLinksConfigurationsDir() {
+    return quickLinksConfigurationsDir;
+  }
+
+  public void setQuickLinksConfigurationsDir(String quickLinksConfigurationsDir) {
+    this.quickLinksConfigurationsDir = quickLinksConfigurationsDir;
+  }
+
+  public List<QuickLinksConfigurationInfo> getQuickLinksConfigurations() {
+    return quickLinksConfigurations;
+  }
+
+  public void setQuickLinksConfigurations(List<QuickLinksConfigurationInfo> quickLinksConfigurations) {
+    this.quickLinksConfigurations = quickLinksConfigurations;
+  }
+
+  public Map<String, QuickLinksConfigurationInfo> getQuickLinksConfigurationsMap() {
+    if (quickLinksConfigurationsMap == null) {
+      synchronized (this) {
+      }
+      if (quickLinksConfigurationsMap == null) {
+        Map<String, QuickLinksConfigurationInfo> tmp = new TreeMap<String, QuickLinksConfigurationInfo>();
+        if (quickLinksConfigurations != null) {
+          for (QuickLinksConfigurationInfo quickLinksConfiguration : quickLinksConfigurations) {
+            tmp.put(quickLinksConfiguration.getFileName(), quickLinksConfiguration);
+          }
+        }
+        quickLinksConfigurationsMap = tmp;
+      }
+    }
+    return quickLinksConfigurationsMap;
+  }
+
+  public void setQuickLinksConfigurationsMap(Map<String, QuickLinksConfigurationInfo> quickLinksConfigurationsMap) {
+    this.quickLinksConfigurationsMap = quickLinksConfigurationsMap;
+  }
 
   public List<ServicePropertyInfo> getServicePropertyList() {
     return servicePropertyList;

+ 60 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Check.java

@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+package org.apache.ambari.server.state.quicklinks;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Check{
+  @JsonProperty("property")
+  private String property;
+
+  @JsonProperty("desired")
+  private String desired;
+
+  @JsonProperty("site")
+  private String site;
+
+  public String getProperty() {
+    return property;
+  }
+
+  public void setProperty(String property) {
+    this.property = property;
+  }
+
+  public String getDesired() {
+    return desired;
+  }
+
+  public void setDesired(String desired) {
+    this.desired = desired;
+  }
+
+  public String getSite() {
+    return site;
+  }
+
+  public void setSite(String site) {
+    this.site = site;
+  }
+}

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

@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+package org.apache.ambari.server.state.quicklinks;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Link{
+  @JsonProperty("name")
+  private String name;
+
+  @JsonProperty("label")
+  private String label;
+
+  @JsonProperty("requires_user_name")
+  private String requiresUserName;
+
+  @JsonProperty("url")
+  private String url;
+
+  @JsonProperty("template")
+  private String template;
+
+  @JsonProperty("port")
+  private Port port;
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getLabel() {
+    return label;
+  }
+
+  public void setLabel(String label) {
+    this.label = label;
+  }
+
+  public String getUrl() {
+    return url;
+  }
+
+  public void setUrl(String url) {
+    this.url = url;
+  }
+
+  public String getTemplate() {
+    return template;
+  }
+
+  public void setTemplate(String template) {
+    this.template = template;
+  }
+
+  public String getRequiresUserName() {
+    return requiresUserName;
+  }
+
+  public void setRequiresUserName(String requiresUserName) {
+    this.requiresUserName = requiresUserName;
+  }
+
+  public Port getPort() {
+    return port;
+  }
+
+  public void setPort(Port port) {
+    this.port = port;
+  }
+
+  public boolean isRemoved(){
+    //treat a link as removed if the section only contains a name
+    return (null == port && null == url && null == template && null == label && null == requiresUserName);
+  }
+
+  public void mergeWithParent(Link parentLink) {
+    if (null == parentLink)
+        return;
+
+    /* merge happens when a child link has some infor but not all of them.
+     * If a child link has nothing but a name, it's treated as being removed from the link list
+     */
+    if(null == template && null != parentLink.getTemplate())
+      template = parentLink.getTemplate();
+
+    if(null == label && null != parentLink.getLabel())
+      label = parentLink.getLabel();
+
+    if(null == url && null != parentLink.getUrl())
+      url = parentLink.getUrl();
+
+    if(null == template && null != parentLink.getTemplate())
+      template = parentLink.getTemplate();
+
+    if(null == requiresUserName && null != parentLink.getRequiresUserName())
+      requiresUserName = parentLink.getRequiresUserName();
+
+    if(null == port){
+        port = parentLink.getPort();
+    } else {
+      port.mergetWithParent(parentLink.getPort());
+    }
+  }
+}

+ 116 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Port.java

@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+package org.apache.ambari.server.state.quicklinks;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Port{
+  @JsonProperty("http_property")
+  private String httpProperty;
+
+  @JsonProperty("http_default_port")
+  private String httpDefaultPort;
+
+  @JsonProperty("https_property")
+  private String httpsProperty;
+
+  @JsonProperty("https_default_port")
+  private String httpsDefaultPort;
+
+  @JsonProperty("regex")
+  private String regex;
+
+  @JsonProperty("site")
+  private String site;
+
+  public String getHttpProperty() {
+    return httpProperty;
+  }
+
+  public void setHttpProperty(String httpProperty) {
+    this.httpProperty = httpProperty;
+  }
+
+  public String getHttpDefaultPort() {
+    return httpDefaultPort;
+  }
+
+  public void setHttpDefaultPort(String httpDefaultPort) {
+    this.httpDefaultPort = httpDefaultPort;
+  }
+
+  public String getHttpsProperty() {
+    return httpsProperty;
+  }
+
+  public void setHttpsProperty(String httpsProperty) {
+    this.httpsProperty = httpsProperty;
+  }
+
+  public String getHttpsDefaultPort() {
+    return httpsDefaultPort;
+  }
+
+  public void setHttpsDefaultPort(String httpsDefaultPort) {
+    this.httpsDefaultPort = httpsDefaultPort;
+  }
+
+  public String getRegex() {
+    return regex;
+  }
+
+  public void setRegex(String regex) {
+    this.regex = regex;
+  }
+
+  public String getSite() {
+    return site;
+  }
+
+  public void setSite(String site) {
+    this.site = site;
+  }
+
+  public void mergetWithParent(Port parentPort){
+    if(null == parentPort)
+      return;
+
+    if(null == httpProperty && null != parentPort.getHttpProperty())
+      httpProperty = parentPort.getHttpProperty();
+
+    if(null == httpDefaultPort && null != parentPort.getHttpDefaultPort())
+      httpDefaultPort = parentPort.getHttpDefaultPort();
+
+    if(null == httpsProperty && null != parentPort.getHttpsProperty())
+      httpsProperty = parentPort.getHttpsProperty();
+
+    if(null == httpsDefaultPort && null != parentPort.getHttpsDefaultPort())
+      httpsDefaultPort = parentPort.getHttpsDefaultPort();
+
+    if(null == regex && null != parentPort.getRegex())
+      regex = parentPort.getRegex();
+
+    if(null == site && null != parentPort.getSite())
+      site = parentPort.getSite();
+  }
+}

+ 51 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/Protocol.java

@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package org.apache.ambari.server.state.quicklinks;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import java.util.List;
+
+@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Protocol{
+  @JsonProperty("type")
+  private String type;
+
+  @JsonProperty("checks")
+  private List<Check> checks;
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public List<Check> getChecks() {
+    return checks;
+  }
+
+  public void setChecks(List<Check> checks) {
+    this.checks = checks;
+  }
+}

+ 78 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/QuickLinks.java

@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package org.apache.ambari.server.state.quicklinks;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class QuickLinks{
+  @JsonProperty("description")
+  private String description;
+  @JsonProperty("name")
+  private String name;
+  @JsonProperty("configuration")
+  private QuickLinksConfiguration quickLinksConfiguration;
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public QuickLinksConfiguration getQuickLinksConfiguration() {
+    return quickLinksConfiguration;
+  }
+
+  public void setQuickLinksConfiguration(QuickLinksConfiguration quickLinksConfiguration) {
+    this.quickLinksConfiguration = quickLinksConfiguration;
+  }
+
+  public void mergeWithParent(QuickLinks parent) {
+    if (parent == null) {
+      return;
+    }
+
+    if (name == null) {
+      name = parent.name;
+    }
+
+    if (description == null) {
+      description = parent.description;
+    }
+
+    if (quickLinksConfiguration == null) {
+      quickLinksConfiguration = parent.quickLinksConfiguration;
+    } else {
+      quickLinksConfiguration.mergeWithParent(parent.quickLinksConfiguration);
+    }
+  }
+}

+ 91 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/quicklinks/QuickLinksConfiguration.java

@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package org.apache.ambari.server.state.quicklinks;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class QuickLinksConfiguration{
+  @JsonProperty("protocol")
+  private Protocol protocol;
+
+  @JsonProperty("links")
+  private List<Link> links;
+
+  public Protocol getProtocol() {
+    return protocol;
+  }
+
+  public void setProtocol(Protocol protocol) {
+    this.protocol = protocol;
+  }
+
+  public List<Link> getLinks() {
+    return links;
+  }
+
+  public void setLinks(List<Link> links) {
+    this.links = links;
+  }
+
+  public void mergeWithParent(QuickLinksConfiguration parent) {
+    if (parent == null) {
+      return;
+    }
+
+    //protocol uses override merge, if the child has it, then use it
+    if(protocol == null)
+      protocol = parent.getProtocol();
+
+    if (links == null) {
+      links = parent.getLinks();
+    } else if (parent.getLinks() != null) {
+      links = mergeLinks(parent.getLinks(), links);
+    }
+  }
+
+  private List<Link> mergeLinks(List<Link> parentLinks, List<Link> childLinks) {
+    Map<String, Link> mergedLinks = new HashMap<String, Link>();
+
+    for (Link parentLink : parentLinks) {
+      mergedLinks.put(parentLink.getName(), parentLink);
+    }
+
+    for (Link childLink : childLinks) {
+      if (childLink.getName() != null) {
+        if(childLink.isRemoved()){
+          mergedLinks.remove(childLink.getName());
+        } else {
+          Link parentLink = mergedLinks.get(childLink.getName());
+          childLink.mergeWithParent(parentLink);
+          mergedLinks.put(childLink.getName(), childLink);
+        }
+      }
+    }
+    return new ArrayList<Link>(mergedLinks.values());
+  }
+}

+ 6 - 0
ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/metainfo.xml

@@ -114,6 +114,12 @@
         <config-type>oozie-site</config-type>
       </excluded-config-types>
 
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
   </services>
 </metainfo>

+ 28 - 0
ambari-server/src/main/resources/common-services/FALCON/0.5.0.2.1/quicklinks/quicklinks.json

@@ -0,0 +1,28 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"HTTP_ONLY"
+    },
+
+    "links": [
+      {
+        "name": "falcon_web_ui",
+        "label": "Falcon Web UI",
+        "requires_user_name": "true",
+        "url":"%@://%@:%@/index.html?user.name=%@",
+        "template":"%@://%@:%@/index.html?user.name=%@",
+        "port":{
+          "http_property": "falcon_port",
+          "http_default_port": "15000",
+          "https_property": "falcon_port",
+          "https_default_port": "15000",
+          "regex": "^(\\d+)$",
+          "site": "falcon-env"
+        }
+      }
+    ]
+  }
+}

+ 6 - 0
ambari-server/src/main/resources/common-services/STORM/0.9.1.2.1/metainfo.xml

@@ -135,6 +135,12 @@
         <config-type>zookeeper-env</config-type>
         <config-type>zoo.cfg</config-type>
       </configuration-dependencies>
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
   </services>
 </metainfo>

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

@@ -0,0 +1,28 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"HTTP_ONLY"
+    },
+
+    "links": [
+      {
+        "name": "storm_ui",
+        "label": "Storm UI",
+        "requires_user_name": "false",
+        "url":"%@://%@:%@/",
+        "template":"%@://%@:%@/",
+        "port":{
+          "http_property": "ui.port",
+          "http_default_port": "8744",
+          "https_property": "ui.port",
+          "https_default_port": "8744",
+          "regex": "^(\\d+)$",
+          "site": "storm-site"
+        }
+      }
+    ]
+  }
+}

+ 7 - 0
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/OOZIE/metainfo.xml

@@ -22,5 +22,12 @@
       <name>OOZIE</name>
       <extends>common-services/OOZIE/4.0.0.2.0</extends>
     </service>
+
+    <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
   </services>
 </metainfo>

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

@@ -0,0 +1,45 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"https",
+      "checks":[
+        {
+          "property":"oozie.https.port",
+          "desired":"EXISTS",
+          "site":"oozie-site"
+        },
+        {
+          "property":"oozie.https.keystore.file",
+          "desired":"EXISTS",
+          "site":"oozie-site"
+        },
+        {
+          "property":"oozie.https.keystore.pass",
+          "desired":"EXISTS",
+          "site":"oozie-site"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "resourcemanager_ui",
+        "label": "Oozie Web UI",
+        "requires_user_name": "true",
+        "url":"%@://%@:%@/oozie?user.name=%@",
+        "template":"%@://%@:%@/oozie?user.name=%@",
+        "port":{
+          "http_property": "oozie.base.url",
+          "http_default_port": "11000",
+          "https_property": "oozie.base.url",
+          "https_default_port": "11443",
+          "regex": "\\w*:(\\d+)",
+          "site": "oozie-site"
+        }
+      }
+    ]
+  }
+}

+ 6 - 0
ambari-server/src/main/resources/stacks/HDP/2.2/services/RANGER/metainfo.xml

@@ -54,6 +54,12 @@
           </packages>
         </osSpecific>
       </osSpecifics>
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
   </services>
 </metainfo>

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

@@ -0,0 +1,35 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"https",
+      "checks":[
+        {
+          "property":"https.enabled",
+          "desired":"true",
+          "site":"ranger-site"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "ranger_admin_ui",
+        "label": "Ranger Admin UI",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@",
+        "template": "%@://%@:%@",
+        "port":{
+          "http_property": "http.service.port",
+          "http_default_port": "6080",
+          "https_property": "https.service.port",
+          "https_default_port": "6182",
+          "regex": "(\\d*)+",
+          "site": "ranger-site"
+        }
+      }
+    ]
+  }
+}

+ 6 - 0
ambari-server/src/main/resources/stacks/HDP/2.2/services/SPARK/metainfo.xml

@@ -49,6 +49,12 @@
           </packages>
         </osSpecific>
       </osSpecifics>
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
   </services>
 </metainfo>

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

@@ -0,0 +1,28 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"HTTP_ONLY"
+    },
+
+    "links": [
+      {
+        "name": "spark_history_server_ui",
+        "label": "Spark History Server UI",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@",
+        "template": "%@://%@:%@",
+        "port":{
+          "http_property": "spark.history.ui.port",
+          "http_default_port": "18080",
+          "https_property": "spark.history.ui.port",
+          "https_default_port": "18080",
+          "regex": "^(\\d+)$",
+          "site": "spark-defaults"
+        }
+      }
+    ]
+  }
+}

+ 6 - 1
ambari-server/src/main/resources/stacks/HDP/2.3/services/ACCUMULO/metainfo.xml

@@ -40,7 +40,12 @@
           </packages>
         </osSpecific>
       </osSpecifics>
-
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
   </services>
 </metainfo>

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

@@ -0,0 +1,40 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"https",
+      "checks":[
+        {
+          "property":"monitor.ssl.keyStore",
+          "desired":"EXIST",
+          "site":"accumulo-site"
+        },
+        {
+          "property":"monitor.ssl.trustStore",
+          "desired":"EXIST",
+          "site":"accumulo-site"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "accumulo_monitor_ui",
+        "label": "Accumulo Monitor UI",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@/",
+        "template": "%@://%@:%@/",
+        "port":{
+          "http_property": "monitor.port.client",
+          "http_default_port": "50095",
+          "https_property": "monitor.port.client",
+          "https_default_port": "50095",
+          "regex": "^(\\d+)$",
+          "site": "accumulo-site"
+        }
+      }
+    ]
+  }
+}

+ 6 - 0
ambari-server/src/main/resources/stacks/HDP/2.3/services/ATLAS/metainfo.xml

@@ -40,6 +40,12 @@
           </packages>
         </osSpecific>
       </osSpecifics>
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
   </services>
 </metainfo>

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

@@ -0,0 +1,35 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"https",
+      "checks":[
+        {
+          "property":"metadata.enableTLS",
+          "desired":"true",
+          "site":"application-properties"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "atlas_dashboard",
+        "label": "Atlas Dashboard",
+        "requires_user_name": "true",
+        "url": "%@://%@:%@/#!/search?user.name=%@",
+        "template": "%@://%@:%@/#!/search?user.name=%@",
+        "port":{
+          "http_property": "atlas.server.http.port",
+          "http_default_port": "21000",
+          "https_property": "atlas.server.https.port",
+          "https_default_port": "21443",
+          "regex": "^(\\d+)$",
+          "site": "application-properties"
+        }
+      }
+    ]
+  }
+}

+ 6 - 0
ambari-server/src/main/resources/stacks/HDP/2.3/services/HBASE/metainfo.xml

@@ -68,6 +68,12 @@
         </theme>
       </themes>
 
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
   </services>
 </metainfo>

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

@@ -0,0 +1,103 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"http"
+    },
+
+    "links": [
+      {
+        "name": "hbase_master_ui",
+        "label": "HBase Master UI",
+        "url":"%@://%@:%@/master-status",
+        "requires_user_name": "false",
+        "template":"%@://%@:%@/master-status",
+        "port":{
+          "http_property": "hbase.master.info.port",
+          "http_default_port": "60010",
+          "https_property": "hbase.master.info.port",
+          "https_default_port": "60443",
+          "regex": "",
+          "site": "hbase-site"
+        }
+      },
+      {
+        "name": "hbase_logs",
+        "label": "HBase Logs",
+        "url":"%@://%@:%@/logs",
+        "requires_user_name": "false",
+        "template":"%@://%@:%@/logs",
+        "port":{
+          "http_property": "hbase.master.info.port",
+          "http_default_port": "60010",
+          "https_property": "hbase.master.info.port",
+          "https_default_port": "60443",
+          "regex": "",
+          "site": "hbase-site"
+        }
+      },
+      {
+        "name": "zookeeper_info",
+        "label": "Zookeeper Info",
+        "url":"%@://%@:%@/zk.jsp",
+        "requires_user_name": "false",
+        "template":"%@://%@:%@/zk.jsp",
+        "port":{
+          "http_property": "hbase.master.info.port",
+          "http_default_port": "60010",
+          "https_property": "hbase.master.info.port",
+          "https_default_port": "60443",
+          "regex": "",
+          "site": "hbase-site"
+        }
+      },
+      {
+        "name": "hbase_master_jmx",
+        "label": "HBase Master JMX",
+        "url":"%@://%@:%@/jmx",
+        "requires_user_name": "false",
+        "template":"%@://%@:%@/jmx",
+        "port":{
+          "http_property": "hbase.master.info.port",
+          "http_default_port": "60010",
+          "https_property": "hbase.master.info.port",
+          "https_default_port": "60443",
+          "regex": "",
+          "site": "hbase-site"
+        }
+      },
+      {
+        "name": "debug_dump",
+        "label": "Debug Dump",
+        "url":"%@://%@:%@/dump",
+        "requires_user_name": "false",
+        "template":"%@://%@:%@/dump",
+        "port":{
+          "http_property": "hbase.master.info.port",
+          "http_default_port": "60010",
+          "https_property": "hbase.master.info.port",
+          "https_default_port": "60443",
+          "regex": "",
+          "site": "hbase-site"
+        }
+      },
+      {
+        "name": "thread_stacks",
+        "label": "Thread Stacks",
+        "url":"%@://%@:%@/stacks",
+        "requires_user_name": "false",
+        "template":"%@://%@:%@/stacks",
+        "port":{
+          "http_property": "hbase.master.info.port",
+          "http_default_port": "60010",
+          "https_property": "hbase.master.info.port",
+          "https_default_port": "60443",
+          "regex": "",
+          "site": "hbase-site"
+        }
+      }
+    ]
+  }
+}

+ 6 - 1
ambari-server/src/main/resources/stacks/HDP/2.3/services/HDFS/metainfo.xml

@@ -117,7 +117,12 @@
           </packages>
         </osSpecific>
       </osSpecifics>
-
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
   </services>
 </metainfo>

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

@@ -0,0 +1,80 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"https",
+      "checks":[
+        {
+          "property":"dfs.http.policy",
+          "desired":"HTTPS_ONLY",
+          "site":"hdfs-site"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "namenode_ui",
+        "label": "NameNode UI",
+        "url":"%@://%@:%@",
+        "requires_user_name": "false",
+        "template":"%@://%@:%@",
+        "port":{
+          "http_property": "dfs.namenode.http-address",
+          "http_default_port": "50070",
+          "https_property": "dfs.namenode.https-address",
+          "https_default_port": "50470",
+          "regex": "\\w*:(\\d+)",
+          "site": "hdfs-site"
+        }
+      },
+      {
+        "name": "namenode_logs",
+        "label": "NameNode Logs",
+        "url":"%@://%@:%@/logs",
+        "requires_user_name": "false",
+        "template":"%@://%@:%@/logs",
+        "port":{
+          "http_property": "dfs.namenode.http-address",
+          "http_default_port": "50070",
+          "https_property": "dfs.namenode.https-address",
+          "https_default_port": "50470",
+          "regex": "\\w*:(\\d+)",
+          "site": "hdfs-site"
+        }
+      },
+      {
+        "name": "namenode_jmx",
+        "label": "NameNode JMX",
+        "url":"%@://%@:%@/jmx",
+        "requires_user_name": "false",
+        "template":"%@://%@:%@/jmx",
+        "port":{
+          "http_property": "dfs.namenode.http-address",
+          "http_default_port": "50070",
+          "https_property": "dfs.namenode.https-address",
+          "https_default_port": "50470",
+          "regex": "\\w*:(\\d+)",
+          "site": "hdfs-site"
+        }
+      },
+      {
+        "name": "Thread Stacks",
+        "label": "Thread Stacks",
+        "url":"%@://%@:%@/stacks",
+        "requires_user_name": "false",
+        "template":"%@://%@:%@/stacks",
+        "port":{
+          "http_property": "dfs.namenode.http-address",
+          "http_default_port": "50070",
+          "https_property": "dfs.namenode.https-address",
+          "https_default_port": "50470",
+          "regex": "\\w*:(\\d+)",
+          "site": "hdfs-site"
+        }
+      }
+    ]
+  }
+}

+ 6 - 1
ambari-server/src/main/resources/stacks/HDP/2.3/services/OOZIE/metainfo.xml

@@ -65,8 +65,13 @@
             </package>
           </packages>
         </osSpecific>
-
       </osSpecifics>
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
   </services>
 </metainfo>

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

@@ -0,0 +1,45 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"https",
+      "checks":[
+        {
+          "property":"oozie.https.port",
+          "desired":"EXISTS",
+          "site":"oozie-site"
+        },
+        {
+          "property":"oozie.https.keystore.file",
+          "desired":"EXISTS",
+          "site":"oozie-site"
+        },
+        {
+          "property":"oozie.https.keystore.pass",
+          "desired":"EXISTS",
+          "site":"oozie-site"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "resourcemanager_ui",
+        "label": "Oozie Web UI",
+        "requires_user_name": "true",
+        "url":"%@://%@:%@/oozie?user.name=%@",
+        "template":"%@://%@:%@/oozie?user.name=%@",
+        "port":{
+          "http_property": "oozie.base.url",
+          "http_default_port": "11000",
+          "https_property": "oozie.base.url",
+          "https_default_port": "11443",
+          "regex": "\\w*:(\\d+)",
+          "site": "oozie-site"
+        }
+      }
+    ]
+  }
+}

+ 6 - 0
ambari-server/src/main/resources/stacks/HDP/2.3/services/RANGER/metainfo.xml

@@ -64,6 +64,12 @@
         <config-type>ranger-ugsync-site</config-type>
       </configuration-dependencies>
 
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
   </services>
 </metainfo>

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

@@ -0,0 +1,40 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"https",
+      "checks":[
+        {
+          "property":"ranger.service.https.attrib.ssl.enabled",
+          "desired":"true",
+          "site":"ranger-admin-site"
+        },
+        {
+          "property":"ranger.service.http.enabled",
+          "desired":"false",
+          "site":"ranger-admin-site"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "ranger_admin_ui",
+        "label": "Ranger Admin UI",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@",
+        "template": "%@://%@:%@",
+        "port":{
+          "http_property": "http.service.port",
+          "http_default_port": "6080",
+          "https_property": "https.service.port",
+          "https_default_port": "6182",
+          "regex": "(\\d*)+",
+          "site": "ranger-site"
+        }
+      }
+    ]
+  }
+}

+ 6 - 0
ambari-server/src/main/resources/stacks/HDP/2.3/services/SPARK/metainfo.xml

@@ -61,6 +61,12 @@
             <config-type>spark-thrift-sparkconf</config-type>
             <config-type>spark-hive-site-override</config-type>
           </configuration-dependencies>
+	      <quickLinksConfigurations>
+	          <quickLinksConfiguration>
+	          <fileName>quicklinks.json</fileName>
+	          <default>true</default>
+	        </quickLinksConfiguration>
+	      </quickLinksConfigurations>
         </service>
     </services>
 </metainfo>

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

@@ -0,0 +1,28 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"HTTP_ONLY"
+    },
+
+    "links": [
+      {
+        "name": "spark_history_server_ui",
+        "label": "Spark History Server UI",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@",
+        "template": "%@://%@:%@",
+        "port":{
+          "http_property": "spark.history.ui.port",
+          "http_default_port": "18080",
+          "https_property": "spark.history.ui.port",
+          "https_default_port": "18080",
+          "regex": "^(\\d+)$",
+          "site": "spark-defaults"
+        }
+      }
+    ]
+  }
+}

+ 13 - 0
ambari-server/src/main/resources/stacks/HDP/2.3/services/YARN/metainfo.xml

@@ -50,6 +50,12 @@
           </packages>
         </osSpecific>
       </osSpecifics>
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
 
     <service>
@@ -74,6 +80,13 @@
         </osSpecific>
       </osSpecifics>
       <configuration-dir>configuration-mapred</configuration-dir>
+      <quickLinksConfigurations-dir>quicklinks-mapred</quickLinksConfigurations-dir>
+      <quickLinksConfigurations>
+        <quickLinksConfiguration>
+          <fileName>quicklinks.json</fileName>
+          <default>true</default>
+        </quickLinksConfiguration>
+      </quickLinksConfigurations>
     </service>
 
   </services>

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

@@ -0,0 +1,80 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"https",
+      "checks":[
+        {
+          "property":"mapreduce.jobhistory.http.policy",
+          "desired":"HTTPS_ONLY",
+          "site":"mapred-site"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "jobhistory_ui",
+        "label": "JobHistory UI",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@",
+        "template": "%@://%@:%@",
+        "port":{
+          "http_property": "mapreduce.jobhistory.webapp.address",
+          "http_default_port": "19888",
+          "https_property": "mapreduce.jobhistory.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "mapred-site"
+        }
+      },
+      {
+        "name": "jobhistory_logs",
+        "label": "JobHistory logs",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@/logs",
+        "template": "%@://%@:%@/logs",
+        "port":{
+          "http_property": "mapreduce.jobhistory.webapp.address",
+          "http_default_port": "19888",
+          "https_property": "mapreduce.jobhistory.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "mapred-site"
+        }
+      },
+      {
+        "name":"jobhistory_jmx",
+        "label":"JobHistory JMX",
+        "requires_user_name":"false",
+        "url":"%@://%@:%@/jmx",
+        "template":"%@://%@/jmx",
+        "port":{
+          "http_property": "mapreduce.jobhistory.webapp.address",
+          "http_default_port": "19888",
+          "https_property": "mapreduce.jobhistory.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "mapred-site"
+        }
+      },
+      {
+        "name":"thread_stacks",
+        "label":"Thread Stacks",
+        "requires_user_name": "false",
+        "url":"%@://%@:%@/stacks",
+        "template":"%@://%@/stacks",
+        "port":{
+          "http_property": "mapreduce.jobhistory.webapp.address",
+          "http_default_port": "19888",
+          "https_property": "mapreduce.jobhistory.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "mapred-site"
+        }
+      }
+    ]
+  }
+}

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

@@ -0,0 +1,80 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"https",
+      "checks":[
+        {
+          "property":"yarn.http.policy",
+          "desired":"HTTPS_ONLY",
+          "site":"yarn-site"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "resourcemanager_ui",
+        "label": "ResourceManager UI",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@",
+        "template": "%@://%@:%@",
+        "port":{
+          "http_property": "yarn.timeline-service.webapp.address",
+          "http_default_port": "8088",
+          "https_property": "yarn.timeline-service.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "yarn-site"
+        }
+      },
+      {
+        "name": "resourcemanager_logs",
+        "label": "ResourceManager logs",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@/logs",
+        "template": "%@://%@:%@/logs",
+        "port":{
+          "http_property": "yarn.timeline-service.webapp.address",
+          "http_default_port": "8088",
+          "https_property": "yarn.timeline-service.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "yarn-site"
+        }
+      },
+      {
+        "name": "resourcemanager_jmx",
+        "label":"ResourceManager JMX",
+        "requires_user_name": "false",
+        "url":"%@://%@:%@/jmx",
+        "template":"%@://%@:%@/jmx",
+        "port":{
+          "http_property": "yarn.timeline-service.webapp.address",
+          "http_default_port": "8088",
+          "https_property": "yarn.timeline-service.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "yarn-site"
+        }
+      },
+      {
+        "name": "thread_stacks",
+        "label":"Thread Stacks",
+        "requires_user_name": "false",
+        "url":"%@://%@:%@/stacks",
+        "template":"%@://%@:%@/stacks",
+        "port":{
+          "http_property": "yarn.timeline-service.webapp.address",
+          "http_default_port": "8088",
+          "https_property": "yarn.timeline-service.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "yarn-site"
+        }
+      }
+    ]
+  }
+}

+ 1 - 1
ambari-server/src/test/java/org/apache/ambari/server/api/resources/StackServiceResourceDefinitionTest.java

@@ -42,7 +42,7 @@ public class StackServiceResourceDefinitionTest {
     ResourceDefinition def = new StackServiceResourceDefinition();
 
     Set<SubResourceDefinition> subResources = def.getSubResourceDefinitions();
-    assertEquals(4, subResources.size());
+    assertEquals(5, subResources.size());
 
     boolean configReturned = false;
     boolean componentReturned = false;

+ 128 - 0
ambari-server/src/test/java/org/apache/ambari/server/stack/QuickLinksConfigurationModuleTest.java

@@ -0,0 +1,128 @@
+/**
+ * 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.
+ */
+
+package org.apache.ambari.server.stack;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.state.quicklinks.Check;
+import org.apache.ambari.server.state.quicklinks.Link;
+import org.apache.ambari.server.state.quicklinks.Port;
+import org.apache.ambari.server.state.quicklinks.Protocol;
+import org.apache.ambari.server.state.quicklinks.QuickLinks;
+import org.apache.ambari.server.state.quicklinks.QuickLinksConfiguration;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class QuickLinksConfigurationModuleTest {
+
+  @Test
+  public void testResolveInherit() throws Exception{
+    QuickLinks[] results = resolveQuickLinks("parent_quicklinks.json", "child_quicklinks_to_inherit.json");
+    QuickLinks parentQuickLinks = results[0];
+    QuickLinks childQuickLinks = results[1];
+
+    //resolved quicklinks configuration
+    QuickLinksConfiguration childQuickLinksConfig = childQuickLinks.getQuickLinksConfiguration();
+    assertNotNull(childQuickLinksConfig);
+
+    //inherit links
+    List<Link> links = childQuickLinksConfig.getLinks();
+    assertNotNull(links);
+    assertEquals(4, links.size());
+    assertEquals(4, parentQuickLinks.getQuickLinksConfiguration().getLinks().size());
+
+    //inherit protocol
+    Protocol protocol = childQuickLinksConfig.getProtocol();
+    assertNotNull(protocol);
+    assertEquals("https", protocol.getType());
+    assertEquals(1, protocol.getChecks().size());
+  }
+
+  @Test
+  public void testResolveMerge() throws Exception {
+    QuickLinks[] results = resolveQuickLinks("parent_quicklinks.json", "child_quicklinks_to_merge.json");
+    QuickLinks parentQuickLinks = results[0];
+    QuickLinks childQuickLinks = results[1];
+
+    //resolved quicklinks configuration
+    QuickLinksConfiguration childQuickLinksConfig = childQuickLinks.getQuickLinksConfiguration();
+    assertNotNull(childQuickLinksConfig);
+
+    //merged links
+    List<Link> links = childQuickLinksConfig.getLinks();
+    assertNotNull(links);
+    assertEquals(7, links.size());
+    assertEquals(4, parentQuickLinks.getQuickLinksConfiguration().getLinks().size());
+  }
+
+  @Test
+  public void testResolveOverride() throws Exception{
+    QuickLinks[] results = resolveQuickLinks("parent_quicklinks.json", "child_quicklinks_to_override.json");
+    QuickLinks parentQuickLinks = results[0];
+    QuickLinks childQuickLinks = results[1];
+
+    //resolved quicklinks configuration
+    QuickLinksConfiguration childQuickLinksConfig = childQuickLinks.getQuickLinksConfiguration();
+    assertNotNull(childQuickLinksConfig);
+
+    //links
+    List<Link> links = childQuickLinksConfig.getLinks();
+    assertNotNull(links);
+    assertEquals(7, links.size());
+    assertEquals(4, parentQuickLinks.getQuickLinksConfiguration().getLinks().size());
+    boolean hasLink = false;
+    for(Link link: links){
+      String name = link.getName();
+      if("thread_stacks".equals(name)){
+        hasLink = true;
+        Port port = link.getPort();
+        assertEquals("mapred-site", port.getSite());
+      }
+    }
+    assertTrue(hasLink);
+
+    //protocol
+    Protocol protocol = childQuickLinksConfig.getProtocol();
+    assertNotNull(protocol);
+    assertEquals("http", protocol.getType());
+    assertEquals(3, protocol.getChecks().size());
+    List<Check> checks = protocol.getChecks();
+    for(Check check: checks){
+      assertEquals("mapred-site", check.getSite());
+    }
+  }
+
+  private QuickLinks[] resolveQuickLinks(String parentJson, String childJson) throws AmbariException{
+    File parentQuiclinksFile = new File(this.getClass().getClassLoader().getResource(parentJson).getFile());
+    File childQuickLinksFile = new File(this.getClass().getClassLoader().getResource(childJson).getFile());
+
+    QuickLinksConfigurationModule parentModule = new QuickLinksConfigurationModule(parentQuiclinksFile);
+    QuickLinksConfigurationModule childModule = new QuickLinksConfigurationModule(childQuickLinksFile);
+
+    childModule.resolve(parentModule, null, null);
+
+    QuickLinks parentQuickLinks = parentModule.getModuleInfo().getQuickLinksConfigurationMap().get(QuickLinksConfigurationModule.QUICKLINKS_CONFIGURATION_KEY);
+    QuickLinks childQuickLinks = childModule.getModuleInfo().getQuickLinksConfigurationMap().get(QuickLinksConfigurationModule.QUICKLINKS_CONFIGURATION_KEY);
+
+    return new QuickLinks[]{parentQuickLinks, childQuickLinks};
+  }
+}

+ 7 - 0
ambari-server/src/test/resources/child_quicklinks_to_inherit.json

@@ -0,0 +1,7 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "links": []
+  }
+}

+ 65 - 0
ambari-server/src/test/resources/child_quicklinks_to_merge.json

@@ -0,0 +1,65 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"http",
+      "checks":[
+        {
+          "property":"mapreduce.jobhistory.http.policy",
+          "desired":"HTTP_ONLY",
+          "site":"mapred-site"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "jobhistory_ui",
+        "label": "JobHistory UI",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@",
+        "template": "%@://%@:%@",
+        "port":{
+          "http_property": "mapreduce.jobhistory.webapp.address",
+          "http_default_port": "19888",
+          "https_property": "mapreduce.jobhistory.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "mapred-site"
+        }
+      },
+      {
+        "name": "jobhistory_logs",
+        "label": "JobHistory logs",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@/logs",
+        "template": "%@://%@:%@/logs",
+        "port":{
+          "http_property": "mapreduce.jobhistory.webapp.address",
+          "http_default_port": "19888",
+          "https_property": "mapreduce.jobhistory.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "mapred-site"
+        }
+      },
+      {
+        "name":"jobhistory_jmx",
+        "label":"JobHistory JMX",
+        "requires_user_name":"false",
+        "url":"%@://%@:%@/jmx",
+        "template":"%@://%@/jmx",
+        "port":{
+          "http_property": "mapreduce.jobhistory.webapp.address",
+          "http_default_port": "19888",
+          "https_property": "mapreduce.jobhistory.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "mapred-site"
+        }
+      }
+    ]
+  }
+}

+ 90 - 0
ambari-server/src/test/resources/child_quicklinks_to_override.json

@@ -0,0 +1,90 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"http",
+      "checks":[
+        {
+          "property":"mapreduce.jobhistory.http.policy",
+          "desired":"HTTPS_ONLY",
+          "site":"mapred-site"
+        },
+        {
+          "property":"ssl.store.pach",
+          "desired":"EXIST",
+          "site":"mapred-site"
+        },
+        {
+          "property":"ssl.store.password",
+          "desired":"EXIST",
+          "site":"mapred-site"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "jobhistory_ui",
+        "label": "JobHistory UI",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@",
+        "template": "%@://%@:%@",
+        "port":{
+          "http_property": "mapreduce.jobhistory.webapp.address",
+          "http_default_port": "19888",
+          "https_property": "mapreduce.jobhistory.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "mapred-site"
+        }
+      },
+      {
+        "name": "jobhistory_logs",
+        "label": "JobHistory logs",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@/logs",
+        "template": "%@://%@:%@/logs",
+        "port":{
+          "http_property": "mapreduce.jobhistory.webapp.address",
+          "http_default_port": "19888",
+          "https_property": "mapreduce.jobhistory.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "mapred-site"
+        }
+      },
+      {
+        "name":"jobhistory_jmx",
+        "label":"JobHistory JMX",
+        "requires_user_name":"false",
+        "url":"%@://%@:%@/jmx",
+        "template":"%@://%@/jmx",
+        "port":{
+          "http_property": "mapreduce.jobhistory.webapp.address",
+          "http_default_port": "19888",
+          "https_property": "mapreduce.jobhistory.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "mapred-site"
+        }
+      },
+      {
+        "name":"thread_stacks",
+        "label":"Thread Stacks",
+        "requires_user_name": "false",
+        "url":"%@://%@:%@/stacks",
+        "template":"%@://%@/stacks",
+        "port":{
+          "http_property": "mapreduce.jobhistory.webapp.address",
+          "http_default_port": "19888",
+          "https_property": "mapreduce.jobhistory.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "mapred-site"
+        }
+      }
+    ]
+  }
+}

+ 80 - 0
ambari-server/src/test/resources/parent_quicklinks.json

@@ -0,0 +1,80 @@
+{
+  "name": "default",
+  "description": "default quick links configuration",
+  "configuration": {
+    "protocol":
+    {
+      "type":"https",
+      "checks":[
+        {
+          "property":"yarn.http.policy",
+          "desired":"HTTPS_ONLY",
+          "site":"yarn-site"
+        }
+      ]
+    },
+
+    "links": [
+      {
+        "name": "resourcemanager_ui",
+        "label": "ResourceManager UI",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@",
+        "template": "%@://%@:%@",
+        "port":{
+          "http_property": "yarn.timeline-service.webapp.address",
+          "http_default_port": "8088",
+          "https_property": "yarn.timeline-service.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "yarn-site"
+        }
+      },
+      {
+        "name": "resourcemanager_logs",
+        "label": "ResourceManager logs",
+        "requires_user_name": "false",
+        "url": "%@://%@:%@/logs",
+        "template": "%@://%@:%@/logs",
+        "port":{
+          "http_property": "yarn.timeline-service.webapp.address",
+          "http_default_port": "8088",
+          "https_property": "yarn.timeline-service.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "yarn-site"
+        }
+      },
+      {
+        "name": "resourcemanager_jmx",
+        "label":"ResourceManager JMX",
+        "requires_user_name": "false",
+        "url":"%@://%@:%@/jmx",
+        "template":"%@://%@:%@/jmx",
+        "port":{
+          "http_property": "yarn.timeline-service.webapp.address",
+          "http_default_port": "8088",
+          "https_property": "yarn.timeline-service.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "yarn-site"
+        }
+      },
+      {
+        "name": "thread_stacks",
+        "label":"Thread Stacks",
+        "requires_user_name": "false",
+        "url":"%@://%@:%@/stacks",
+        "template":"%@://%@:%@/stacks",
+        "port":{
+          "http_property": "yarn.timeline-service.webapp.address",
+          "http_default_port": "8088",
+          "https_property": "yarn.timeline-service.webapp.https.address",
+          "https_default_port": "8090",
+          "regex": "\\w*:(\\d+)",
+          "site": "yarn-site"
+        }
+      }
+    ]
+  }
+}

+ 102 - 0
ambari-web/app/assets/data/configurations/quicklinks.json

@@ -0,0 +1,102 @@
+{
+  "href" : "http://c6401.ambari.apache.org:8080/api/v1/stacks/HDP/versions/2.3/services/YARN/quicklinks?QuickLinkInfo/default=true&fields=*",
+  "items" : [
+    {
+      "href" : "http://c6401.ambari.apache.org:8080/api/v1/stacks/HDP/versions/2.3/services/YARN/quicklinks/quicklinks.json",
+      "QuickLinkInfo" : {
+        "default" : true,
+        "file_name" : "quicklinks.json",
+        "service_name" : "YARN",
+        "stack_name" : "HDP",
+        "stack_version" : "2.3",
+        "quicklink_data" : {
+          "QuickLinksConfiguration" : {
+            "description" : "default quick links configuration",
+            "name" : "default",
+            "configuration" : {
+              "protocol" : {
+                "type" : "https",
+                "checks" : [
+                  {
+                    "property" : "yarn.http.policy",
+                    "desired" : "HTTPS_ONLY",
+                    "site" : "yarn-site"
+                  }
+                ]
+              },
+              "links" : [
+                {
+                  "name" : "resourcemanager_ui",
+                  "label" : "ResourceManager UI",
+                  "url" : "%@://%@:%@",
+                  "template" : "%@://%@:%@",
+                  "port" : {
+                    "regex" : "\\w*:(\\d+)",
+                    "site" : "yarn-site",
+                    "http_property" : "yarn.timeline-service.webapp.address",
+                    "http_default_port" : "8088",
+                    "https_property" : "yarn.timeline-service.webapp.https.address",
+                    "https_default_port" : "8090"
+                  },
+                  "removed" : false,
+                  "requires_user_name" : "false",
+                  "knox_url" : "%@://%@:%@/gatway/yarnui/yarn"
+                },
+                {
+                  "name" : "resourcemanager_logs",
+                  "label" : "ResourceManager logs",
+                  "url" : "%@://%@:%@/logs",
+                  "template" : "%@://%@:%@/logs",
+                  "port" : {
+                    "regex" : "\\w*:(\\d+)",
+                    "site" : "yarn-site",
+                    "http_property" : "yarn.timeline-service.webapp.address",
+                    "http_default_port" : "8088",
+                    "https_property" : "yarn.timeline-service.webapp.https.address",
+                    "https_default_port" : "8090"
+                  },
+                  "removed" : false,
+                  "requires_user_name" : "false",
+                  "knox_url" : "%@://%@:%@/gatway/yarnui/yarn/logs"
+                },
+                {
+                  "name" : "resourcemanager_jmx",
+                  "label" : "ResourceManager JMX",
+                  "url" : "%@://%@:%@/jmx",
+                  "template" : "%@://%@:%@/jmx",
+                  "port" : {
+                    "regex" : "\\w*:(\\d+)",
+                    "site" : "yarn-site",
+                    "http_property" : "yarn.timeline-service.webapp.address",
+                    "http_default_port" : "8088",
+                    "https_property" : "yarn.timeline-service.webapp.https.address",
+                    "https_default_port" : "8090"
+                  },
+                  "removed" : false,
+                  "requires_user_name" : "false",
+                  "knox_url" : "%@://%@:%@/gatway/yarnui/yarn/jmx"
+                },
+                {
+                  "name" : "thread_stacks",
+                  "label" : "Thread Stacks",
+                  "url" : "%@://%@:%@/stacks",
+                  "template" : "%@://%@:%@/stacks",
+                  "port" : {
+                    "regex" : "\\w*:(\\d+)",
+                    "site" : "yarn-site",
+                    "http_property" : "yarn.timeline-service.webapp.address",
+                    "http_default_port" : "8088",
+                    "https_property" : "yarn.timeline-service.webapp.https.address",
+                    "https_default_port" : "8090"
+                  },
+                  "removed" : false,
+                  "knox_url" : "%@://%@:%@/gatway/yarnui/yarn/stacks"
+                }
+              ]
+            }
+          }
+        }
+      }
+    }
+  ]
+}

+ 112 - 0
ambari-web/app/assets/data/configurations/quicklinks_services.json

@@ -0,0 +1,112 @@
+{
+  "href" : "http://c6401.ambari.apache.org:8080/api/v1/stacks/HDP/versions/2.3/services?StackServices/service_name.in(YARN)&quicklinks/QuickLinkInfo/default=true&fields=quicklinks/*",
+  "items" : [
+    {
+      "href" : "http://c6401.ambari.apache.org:8080/api/v1/stacks/HDP/versions/2.3/services/YARN",
+      "StackServices" : {
+        "service_name" : "YARN",
+        "stack_name" : "HDP",
+        "stack_version" : "2.3"
+      },
+      "quicklinks" : [
+        {
+          "href" : "http://c6401.ambari.apache.org:8080/api/v1/stacks/HDP/versions/2.3/services/YARN/quicklinks/quicklinks.json",
+          "QuickLinkInfo" : {
+            "default" : true,
+            "file_name" : "quicklinks.json",
+            "service_name" : "YARN",
+            "stack_name" : "HDP",
+            "stack_version" : "2.3",
+            "quicklink_data" : {
+              "QuickLinksConfiguration" : {
+                "description" : "default quick links configuration",
+                "name" : "default",
+                "configuration" : {
+                  "protocol" : {
+                    "type" : "https",
+                    "checks" : [
+                      {
+                        "property" : "yarn.http.policy",
+                        "desired" : "HTTPS_ONLY",
+                        "site" : "yarn-site"
+                      }
+                    ]
+                  },
+                  "links" : [
+                    {
+                      "name" : "resourcemanager_ui",
+                      "label" : "ResourceManager UI",
+                      "url" : "%@://%@:%@",
+                      "template" : "%@://%@:%@",
+                      "port" : {
+                        "regex" : "\\w*:(\\d+)",
+                        "site" : "yarn-site",
+                        "http_property" : "yarn.timeline-service.webapp.address",
+                        "http_default_port" : "8088",
+                        "https_property" : "yarn.timeline-service.webapp.https.address",
+                        "https_default_port" : "8090"
+                      },
+                      "removed" : false,
+                      "requires_user_name" : "false",
+                      "knox_url" : "%@://%@:%@/gatway/yarnui/yarn"
+                    },
+                    {
+                      "name" : "resourcemanager_logs",
+                      "label" : "ResourceManager logs",
+                      "url" : "%@://%@:%@/logs",
+                      "template" : "%@://%@:%@/logs",
+                      "port" : {
+                        "regex" : "\\w*:(\\d+)",
+                        "site" : "yarn-site",
+                        "http_property" : "yarn.timeline-service.webapp.address",
+                        "http_default_port" : "8088",
+                        "https_property" : "yarn.timeline-service.webapp.https.address",
+                        "https_default_port" : "8090"
+                      },
+                      "removed" : false,
+                      "requires_user_name" : "false",
+                      "knox_url" : "%@://%@:%@/gatway/yarnui/yarn/logs"
+                    },
+                    {
+                      "name" : "resourcemanager_jmx",
+                      "label" : "ResourceManager JMX",
+                      "url" : "%@://%@:%@/jmx",
+                      "template" : "%@://%@:%@/jmx",
+                      "port" : {
+                        "regex" : "\\w*:(\\d+)",
+                        "site" : "yarn-site",
+                        "http_property" : "yarn.timeline-service.webapp.address",
+                        "http_default_port" : "8088",
+                        "https_property" : "yarn.timeline-service.webapp.https.address",
+                        "https_default_port" : "8090"
+                      },
+                      "removed" : false,
+                      "requires_user_name" : "false",
+                      "knox_url" : "%@://%@:%@/gatway/yarnui/yarn/jmx"
+                    },
+                    {
+                      "name" : "thread_stacks",
+                      "label" : "Thread Stacks",
+                      "url" : "%@://%@:%@/stacks",
+                      "template" : "%@://%@:%@/stacks",
+                      "port" : {
+                        "regex" : "\\w*:(\\d+)",
+                        "site" : "yarn-site",
+                        "http_property" : "yarn.timeline-service.webapp.address",
+                        "http_default_port" : "8088",
+                        "https_property" : "yarn.timeline-service.webapp.https.address",
+                        "https_default_port" : "8090"
+                      },
+                      "removed" : false,
+                      "knox_url" : "%@://%@:%@/gatway/yarnui/yarn/stacks"
+                    }
+                  ]
+                }
+              }
+            }
+          }
+        }
+      ]
+    }
+  ]
+}

+ 1 - 0
ambari-web/app/mappers.js

@@ -26,6 +26,7 @@ require('mappers/configs/stack_config_properties_mapper');
 require('mappers/configs/config_groups_mapper');
 require('mappers/configs/service_config_version_mapper');
 require('mappers/repository_version_mapper');
+require('mappers/quicklinks_mapper');
 require('mappers/hosts_mapper');
 require('mappers/cluster_mapper');
 require('mappers/racks_mapper');

+ 50 - 0
ambari-web/app/mappers/quicklinks_mapper.js

@@ -0,0 +1,50 @@
+/**
+ * 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.quicklinksMapper = App.QuickDataMapper.create({
+  model: App.QuickLinksConfig,
+
+  config: {
+    "id": "QuickLinkInfo.service_name",
+    "file_name" : "QuickLinkInfo.file_name",
+    "service_name" : "QuickLinkInfo.service_name",
+    "stack_name" : "QuickLinkInfo.stack_name",
+    "stack_version" : "QuickLinkInfo.stack_version",
+    "name" : "QuickLinkInfo.quicklink_data.QuickLinksConfiguration.name",
+    "protocol" : "QuickLinkInfo.quicklink_data.QuickLinksConfiguration.configuration.protocol",
+    "links" : "QuickLinkInfo.quicklink_data.QuickLinksConfiguration.configuration.links"
+  },
+
+  map: function(json){
+    console.time('App.quicklinksMapper execution time');
+
+    var result = [];
+    var linkResult = [];
+
+    json.items.forEach(function(item) {
+      var parseResult = this.parseIt(item, this.get('config'));
+      console.log("parseResult", parseResult);
+      result.push(parseResult);
+    }, this);
+
+    App.store.loadMany(this.get('model'), result);
+    App.store.commit();
+    console.timeEnd('App.quicklinksMapper execution time');
+  }
+});

+ 1 - 0
ambari-web/app/models.js

@@ -34,6 +34,7 @@ require('models/repository');
 require('models/stack_service');
 require('models/stack_service_component');
 require('models/quick_links');
+require('models/quicklinks/quick_links_config');
 require('models/service');
 require('models/service_audit');
 require('models/service/hdfs');

+ 35 - 0
ambari-web/app/models/quicklinks/quick_links_config.js

@@ -0,0 +1,35 @@
+/**
+ * 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.QuickLinksConfig = DS.Model.extend({
+  //general information
+  name: DS.attr('string'),
+  description: DS.attr('string'),
+  fileName: DS.attr('string'),
+  serviceName: DS.attr('string'),
+  stackName: DS.attr('string'),
+  stackVersion: DS.attr('string'),
+  //protocol configurations
+  protocol: DS.attr('object'),
+  //links configurations
+  links: DS.attr('array')
+});
+
+App.QuickLinksConfig.FIXTURES = [];

+ 4 - 4
ambari-web/app/templates/main/service/item.hbs

@@ -18,8 +18,8 @@
 
 {{view App.MainServiceInfoMenuView configTabBinding="view.hasConfigTab" heatmapTabBinding="view.hasHeatmapTab"}}
   <div class="span3 quick-links-wrapper">
-    {{#if view.service.quickLinks.length}}
-      {{#view App.QuickViewLinks contentBinding="view.service"}}
+    {{#view App.QuickViewLinks contentBinding="view.service"}}
+      {{#if view.hasQuickLinksConfiged}}
         <ul class="nav nav-pills move">
           <li class="dropdown quick-links-dropdown">
             <a class="dropdown-toggle" id="quick-links-dropdown-btn" data-toggle="dropdown" href="#">{{t common.quickLinks}}<b class="caret"></b></a>
@@ -52,8 +52,8 @@
            </ul>
           </li>
         </ul>
-      {{/view}}
-    {{/if}}
+      {{/if}}
+    {{/view}}
   </div>
 
 {{#isAuthorized "SERVICE.RUN_CUSTOM_COMMAND, SERVICE.RUN_SERVICE_CHECK, SERVICE.START_STOP, SERVICE.TOGGLE_MAINTENANCE, SERVICE.ENABLE_HA"}}

+ 12 - 0
ambari-web/app/utils/ajax/ajax.js

@@ -636,6 +636,18 @@ var urls = {
     'mock': '/data/configurations/theme_services.json'
   },
 
+  /*************************CONFIG QUICKLINKS****************************************/
+
+  'configs.quicklinksconfig': {
+    'real': '{stackVersionUrl}/services/{serviceName}/quicklinks?QuickLinkInfo/default=true&fields=*',
+    'mock': '/data/configurations/quicklinks.json'
+  },
+
+  'configs.quicklinksconfig.services': {
+    'real': '{stackVersionUrl}/services?StackServices/service_name.in({serviceNames})&quicklinks/QuickLinkInfo/default=true&fields=quicklinks/*',
+    'mock': '/data/configurations/quicklinks_services.json'
+  },
+
   /*************************CONFIG GROUPS***************************************/
 
   'configs.config_groups.load.all': {

+ 254 - 164
ambari-web/app/views/common/quick_view_link_view.js

@@ -23,6 +23,8 @@ App.QuickViewLinks = Em.View.extend({
 
   isLoaded: false,
 
+  hasQuickLinksConfiged: false,
+
   /**
    * service which has blank target of link
    * @type {Array}
@@ -54,7 +56,7 @@ App.QuickViewLinks = Em.View.extend({
   /**
    * list of files that contains properties for enabling/disabling ssl
    */
-  requiredSiteNames: ['hadoop-env', 'yarn-env', 'hbase-env', 'oozie-env', 'mapred-env', 'storm-env', 'falcon-env', 'core-site', 'hdfs-site', 'hbase-site', 'oozie-site', 'yarn-site', 'mapred-site', 'storm-site', 'spark-defaults', 'accumulo-site', 'application-properties', 'ranger-admin-site', 'ranger-site', 'admin-properties'],
+  requiredSiteNames: [],
 
   /**
    * @type {string}
@@ -74,13 +76,14 @@ App.QuickViewLinks = Em.View.extend({
   }.property().volatile(),
 
   didInsertElement: function () {
-    this.setQuickLinks();
+    this.loadQuickLinksConfigurations();
   },
 
   willDestroyElement: function () {
     this.get('configProperties').clear();
     this.get('actualTags').clear();
     this.get('quickLinks').clear();
+    this.get('requiredSiteNames').clear();
   },
 
   /**
@@ -137,6 +140,54 @@ App.QuickViewLinks = Em.View.extend({
     this.getQuickLinksHosts();
   },
 
+  loadQuickLinksConfigurations: function(){
+    var serviceName = this.get('content.serviceName');
+    console.info("Loading quicklinks configurations for " + serviceName);
+    return App.ajax.send({
+      name: 'configs.quicklinksconfig',
+      sender: this,
+      data: {
+        serviceName: serviceName,
+        stackVersionUrl: App.get('stackVersionURL')
+      },
+      success: 'loadQuickLinksConfigSuccessCallback'
+    });
+  },
+
+  loadQuickLinksConfigSuccessCallback: function(data){
+    App.quicklinksMapper.map(data);
+    var quickLinksConfig = this.getQuickLinksConfiguration();
+    if(quickLinksConfig != null){
+      var protocolConfig = Em.get(quickLinksConfig, 'protocol');
+      var checks = Em.get(protocolConfig, 'checks');
+      var sites = ['core-site', 'hdfs-site'];
+      if(checks){
+        checks.forEach(function(check){
+          var protocolConfigSiteProp = Em.get(check, 'site')
+          if (sites.indexOf(protocolConfigSiteProp) < 0){
+            sites.push(protocolConfigSiteProp);
+          }
+        }, this);
+      }
+
+      var links = Em.get(quickLinksConfig, 'links');
+      if(links && links.length > 0){
+        links.forEach(function(link){
+          if(!link.remove){
+            var portConfig = Em.get(link, 'port');
+            var portConfigSiteProp = Em.get(portConfig, 'site');
+            if(sites.indexOf(portConfigSiteProp) < 0){
+              sites.push(portConfigSiteProp);
+            }
+          }
+        }, this);
+
+        this.set('requiredSiteNames', sites);
+        this.setQuickLinks();
+      }
+    }
+  },
+
   /**
    * call for public host names
    * @returns {$.ajax}
@@ -157,8 +208,12 @@ App.QuickViewLinks = Em.View.extend({
   },
 
   setQuickLinksSuccessCallback: function (response) {
-    var hosts = this.getHosts(response, this.get('content.serviceName'));
-    if (hosts.length === 0 || Em.isNone(this.get('content.quickLinks'))) {
+    var serviceName = this.get('content.serviceName');
+    var hosts = this.getHosts(response, serviceName);
+    var hasQuickLinks = this.hasQuickLinksConfig(serviceName);
+    this.set('hasQuickLinksConfiged', hasQuickLinks); // no need to set quicklinks if current service does not have quick links configured...
+
+    if (hosts.length === 0){
       this.setEmptyLinks();
     } else if (hosts.length === 1) {
       this.setSingleHostLinks(hosts, response);
@@ -192,6 +247,73 @@ App.QuickViewLinks = Em.View.extend({
     return App.router.get('configurationController').getConfigsByTags(tags);
   },
 
+  getQuickLinksConfiguration: function(){
+    var serviceName =  this.get('content.serviceName');
+    var self = this;
+    if(self.hasQuickLinksConfig(serviceName)){
+      var quickLinksConfiguration = App.QuickLinksConfig.find().findProperty("id", serviceName);
+      return quickLinksConfiguration;
+    }
+    return null;
+  },
+
+  hasQuickLinksConfig: function(serviceName) {
+    var result = App.QuickLinksConfig.find().findProperty('id', serviceName);
+    if(!result)
+      return false;
+
+    var links = result.get("links");
+    if(links && links.length > 0){
+      var toBeRemoved = 0;
+      links.forEach(function(link){
+        if(link.remove)
+          toBeRemoved++;
+      });
+      return !(links.length  === toBeRemoved);
+    } else {
+      return false;
+    }
+  },
+
+  toAddLink: function(link){
+    var linkRemoved = Em.get(link, 'removed');
+    var template = Em.get(link, 'template');
+    return (template && !linkRemoved);
+  },
+
+  getHostLink: function(link, host, protocol, configProperties, response){
+    var serviceName = this.get('content.serviceName');
+    if (serviceName === 'MAPREDUCE2' && response) {
+      var portConfig = Em.get(link, 'port');
+      var siteName = Em.get(portConfig, 'site');
+      var siteConfigs = this.get('configProperties').findProperty('type', siteName).properties;
+      var hostPortConfigValue = siteConfigs[Em.get(portConfig, protocol + '_config')];
+      if (hostPortConfigValue != null) {
+        var hostPortValue = hostPortConfigValue.match(new RegExp("([\\w\\d.-]*):(\\d+)"));
+        var hostObj = response.items.findProperty('Hosts.host_name', hostPortValue[1]);
+        if (hostObj != null) {
+          host = hostObj.Hosts.public_host_name;
+        }
+      }
+    }
+
+    var linkPort = this.setPort(Em.get(link, 'port'), protocol, configProperties);
+    if (this.toAddLink(link)) {
+      var newItem = {};
+      var requiresUserName = Em.get(link, 'requires_user_name');
+      var template = Em.get(link, 'template');
+        if('true' === requiresUserName){
+          newItem.url = template.fmt(protocol, host, linkPort, App.router.get('loginName'));
+        } else {
+          newItem.url = template.fmt(protocol, host, linkPort);
+        }
+        newItem.label = link.label;
+        return newItem;
+    } else {
+      return null;
+    }
+  },
+
   /**
    * set empty links
    */
@@ -209,42 +331,26 @@ App.QuickViewLinks = Em.View.extend({
    * @param {Array} hosts
    */
   setSingleHostLinks: function (hosts, response) {
-    var quickLinks = this.get('content.quickLinks').map(function (item) {
-      var protocol = this.setProtocol(item.get('serviceName'), this.get('configProperties'), this.get('ambariProperties'), item);
+    var quickLinksConfig = this.getQuickLinksConfiguration();
+    if(quickLinksConfig != null){
+      var quickLinks = [];
+      var configProperties = this.get('configProperties');
+      var protocol = this.setProtocol(configProperties, quickLinksConfig);
       var publicHostName = hosts[0].publicHostName;
-      var port = item.get('http_config') && this.setPort(item, protocol);
-      var siteConfigs = {};
-
-      if (item.get('template')) {
-        if (item.get('serviceName') === 'MAPREDUCE2') {
-          siteConfigs = this.get('configProperties').findProperty('type', item.get('site')).properties;
-          var hostPortConfigValue = siteConfigs[item.get(protocol + '_config')];
-          if (hostPortConfigValue != null) {
-            var hostPortValue = hostPortConfigValue.match(new RegExp("([\\w\\d.-]*):(\\d+)"));
-            var hostObj = response.items.findProperty('Hosts.host_name', hostPortValue[1]);
-            if (hostObj != null) {
-              var publicHostValue = hostObj.Hosts.public_host_name;
-              hostPortConfigValue = "%@:%@".fmt(publicHostValue, hostPortValue[2])
-            }
-          }
-          item.set('url', item.get('template').fmt(protocol, hostPortConfigValue));
-        } else if (item.get('serviceName') === 'RANGER') {
-          siteConfigs = this.get('configProperties').findProperty('type', 'admin-properties').properties;
-          if (siteConfigs['policymgr_external_url']) {
-            // external_url example: "http://c6404.ambari.apache.org:6080"
-            var hostAndPort = siteConfigs['policymgr_external_url'].split('://')[1];
-            item.set('url', protocol + '://' + hostAndPort);
-          } else {
-            item.set('url', item.get('template').fmt(protocol, publicHostName, port));
-          }
-        } else {
-          item.set('url', item.get('template').fmt(protocol, publicHostName, port, App.router.get('loginName')));
+
+      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(newItem != null){
+          quickLinks.push(newItem);
         }
-      }
-      return item;
-    }, this);
-    this.set('quickLinks', quickLinks);
-    this.set('isLoaded', true);
+      }, this);
+      this.set('quickLinks', quickLinks);
+      this.set('isLoaded', true);
+    } else {
+      this.set('quickLinks', []);
+      this.set('isLoaded', false);
+    }
   },
 
   /**
@@ -252,18 +358,31 @@ App.QuickViewLinks = Em.View.extend({
    * @param {Array} hosts
    */
   setMultipleHostLinks: function (hosts) {
+    var quickLinksConfig = this.getQuickLinksConfiguration();
+    if(quickLinksConfig == null){
+      this.set('quickLinksArray', []);
+      this.set('isLoaded', false);
+      return;
+    }
+
     var quickLinksArray = [];
     hosts.forEach(function (host) {
+      var publicHostName = host.publicHostName;
       var quickLinks = [];
-      this.get('content.quickLinks').forEach(function (item) {
-        var newItem = {};
-        var protocol = this.setProtocol(item.get('serviceName'), this.get('configProperties'), this.get('ambariProperties'), item);
-        if (item.get('template')) {
+      var configProperties = this.get('configProperties');
+
+      var protocol = this.setProtocol(configProperties, quickLinksConfig);
+      var serviceName = Em.get(quickLinksConfig, 'serviceName');
+      var links = Em.get(quickLinksConfig, 'links');
+      links.forEach(function(link){
+        var linkRemoved = Em.get(link, 'removed');
+        var template = Em.get(link, 'template');
+        if (template && !linkRemoved) {
           var port;
           var hostNameRegExp = new RegExp('([\\w\\W]*):\\d+');
-          if (item.get('serviceName') === 'HDFS') {
+          if (serviceName === 'HDFS') {
             var config;
-            var configPropertiesObject = this.get('configProperties').findProperty('type', item.get('site'));
+            var configPropertiesObject = configProperties.findProperty('type', 'hdfs-site');
             if (configPropertiesObject && configPropertiesObject.properties) {
               var properties = configPropertiesObject.properties;
               var nameServiceId = properties['dfs.nameservices'];
@@ -280,19 +399,18 @@ App.QuickViewLinks = Em.View.extend({
                 }
               }
             }
-            port = this.setPort(item, protocol, config);
-          } else {
-            port = item.get('http_config') && this.setPort(item, protocol);
+            var portConfig = Em.get(link, 'port');
+            Em.set(portConfig, protocol +'_property', config);
+            Em.set(link, 'port', portConfig)
           }
-          if (item.get('serviceName') === 'OOZIE') {
-            newItem.url = item.get('template').fmt(protocol, host.publicHostName, port, App.router.get('loginName'));
-          } else {
-            newItem.url = item.get('template').fmt(protocol, host.publicHostName, port);
+
+          var newItem = this.getHostLink(link, publicHostName, protocol, configProperties); //quicklink generated for the hbs template
+          if(newItem != null){
+            quickLinks.push(newItem);
           }
-          newItem.label = item.get('label');
         }
-        quickLinks.push(newItem);
       }, this);
+
       if (host.status) {
         quickLinks.set('publicHostNameLabel', Em.I18n.t('quick.links.publicHostName').format(host.publicHostName, host.status));
       } else {
@@ -456,6 +574,31 @@ App.QuickViewLinks = Em.View.extend({
    */
   servicesSupportsHttps: ["HDFS", "HBASE"],
 
+  reverseType: function(type){
+    if("https" === type)
+      return "http";
+    else if("http" === type)
+      return "https"
+  },
+
+  meetDesired: function(configProperties, configType, property, desiredState){
+    var currentConfig = configProperties.findProperty('type', configType);
+    var currentPropertyValue = currentConfig.properties[property];
+    if("NOT_EXIST" === desiredState){
+      if(currentPropertyValue == null)
+        return true;
+      else
+        return false
+    } else if("EXIST" === desiredState){
+      if(currentPropertyValue == null)
+        return false;
+      else
+        return true;
+    } else {
+      return (desiredState === currentPropertyValue)
+    }
+  },
+
   /**
    * setProtocol - if cluster is secure for some services (GANGLIA, MAPREDUCE2, YARN and servicesSupportsHttps)
    * protocol becomes "https" otherwise "http" (by default)
@@ -466,95 +609,51 @@ App.QuickViewLinks = Em.View.extend({
    * @method setProtocol
    * @param item
    */
-  setProtocol: function (serviceName, configProperties, ambariProperties, item) {
+  setProtocol: function (configProperties, item) {
     var hadoopSslEnabled = false;
+
     if (configProperties && configProperties.length > 0) {
       var hdfsSite = configProperties.findProperty('type', 'hdfs-site');
       hadoopSslEnabled = (hdfsSite && Em.get(hdfsSite, 'properties') && hdfsSite.properties['dfs.http.policy'] === 'HTTPS_ONLY');
     }
-    switch (serviceName) {
-      case "YARN":
-        var yarnProperties = configProperties && configProperties.findProperty('type', 'yarn-site');
-        if (yarnProperties && yarnProperties.properties) {
-          if (yarnProperties.properties['yarn.http.policy'] === 'HTTPS_ONLY') {
-            return "https";
-          } else if (yarnProperties.properties['yarn.http.policy'] === 'HTTP_ONLY') {
-            return "http";
-          }
-        }
-        return hadoopSslEnabled ? "https" : "http";
-        break;
-      case "MAPREDUCE2":
-        var mapred2Properties = configProperties && configProperties.findProperty('type', 'mapred-site');
-        if (mapred2Properties && mapred2Properties.properties) {
-          if (mapred2Properties.properties['mapreduce.jobhistory.http.policy'] === 'HTTPS_ONLY') {
-            return "https";
-          } else if (mapred2Properties.properties['mapreduce.jobhistory.http.policy'] === 'HTTP_ONLY') {
-            return "http";
-          }
-        }
-        return hadoopSslEnabled ? "https" : "http";
-        break;
-      case "ACCUMULO":
-        var accumuloProperties = configProperties && configProperties.findProperty('type', 'accumulo-site');
-        if (accumuloProperties && accumuloProperties.properties) {
-          if (accumuloProperties.properties['monitor.ssl.keyStore'] && accumuloProperties.properties['monitor.ssl.trustStore']) {
-            return "https";
-          } else {
-            return "http";
-          }
-        }
-        return "http";
-        break;
-      case "ATLAS":
-        var atlasProperties = configProperties && configProperties.findProperty('type', 'application-properties');
-        if (atlasProperties && atlasProperties.properties) {
-          if (atlasProperties.properties['metadata.enableTLS'] == "true") {
-            return "https";
-          } else {
-            return "http";
-          }
-        }
+
+    var protocolConfig = Em.get(item, 'protocol');
+    if(!protocolConfig){
+      if(hadoopSslEnabled)
+        return "https";
+      else
         return "http";
-        break;
-      case "OOZIE":
-        var site = configProperties.findProperty('type', 'oozie-site');
-        var properties = site && site.properties;
-        var url = properties && properties['oozie.base.url'];
-        var re = new RegExp(item.get('regex'));
-        var portValue = url && url.match(re);
-        var port = portValue && portValue.length && portValue[1];
-        var protocol = 'http';
-        var isHttpsPropertiesEnabled = properties && (properties['oozie.https.port'] ||
-                                                      properties['oozie.https.keystore.file'] ||
-                                                      properties['oozie.https.keystore.pass']);
-        if (port === '11443' || isHttpsPropertiesEnabled) {
-          protocol = 'https';
-        }
-        return protocol;
-        break;
-      case "RANGER":
-        var rangerProperties = configProperties && configProperties.findProperty('type', 'ranger-admin-site');
-        var rangerSiteProperties = configProperties && configProperties.findProperty('type', 'ranger-site');
-        if (rangerProperties && rangerProperties.properties &&
-          rangerProperties.properties['ranger.service.https.attrib.ssl.enabled'] == "true" &&
-          rangerProperties.properties['ranger.service.http.enabled'] == "false") {
-          //HDP2.3
-          return "https";
-        } else if (rangerProperties && rangerProperties.properties &&
-          rangerProperties.properties['ranger.service.https.attrib.ssl.enabled'] == "false" &&
-          rangerProperties.properties['ranger.service.http.enabled'] == "true") {
-          //HDP2.3
-          return "http";
-        } else if (rangerSiteProperties && rangerSiteProperties.properties && rangerSiteProperties.properties['http.enabled'] == "false") {
-          //HDP2.2
-          return "https";
-        } else {
-          return "http";
+    }
+
+    var protocolType = Em.get(protocolConfig, 'type');
+
+    if ("HTTPS_ONLY" === protocolType)
+      return "https";
+    else if ("HTTP_ONLY" === protocolType)
+      return "http";
+    else {
+      var count = 0;
+      var checks = Em.get(protocolConfig, 'checks');
+      if(!checks){
+        if(hadoopSslEnabled)
+          return 'https';
+        else
+          return 'http';
+      }
+      checks.forEach(function(check){
+        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)
+        if(!checkMeet){
+          count++;
         }
-        break;
-      default:
-        return this.get('servicesSupportsHttps').contains(serviceName) && hadoopSslEnabled ? "https" : "http";
+      }, this);
+
+      if(count > 0)
+        return this.reverseType(protocolType);
+      else
+        return protocolType;
     }
   },
 
@@ -566,37 +665,28 @@ App.QuickViewLinks = Em.View.extend({
    * @returns {string}
    * @method setPort
    */
-  setPort: function (item, protocol, config) {
-    var configProperties = this.get('configProperties');
-    var configProp = config || item.get('http_config');
-    var defaultPort = item.get('default_http_port');
-    if (protocol === 'https' && (config || item.get('https_config'))) {
-      configProp = config || item.get('https_config');
-      if (item.get('default_https_port')) {
-        defaultPort = item.get('default_https_port');
-      }
-    }
-    var site = configProperties.findProperty('type', item.get('site'));
-    var propertyValue = site && site.properties && site.properties[configProp];
-    if (!propertyValue) {
-      if (item.get('serviceName') == 'RANGER') {
-        // HDP 2.3
-        var adminSite = configProperties.findProperty('type', 'ranger-admin-site');
-        if (protocol === 'https') {
-          propertyValue = adminSite && adminSite.properties && adminSite.properties['ranger.service.https.port'];
-        } else {
-          propertyValue = adminSite && adminSite.properties && adminSite.properties['ranger.service.http.port'];
-        }
-      }
-    }
+  setPort: function (portConfigs, protocol, configProperties, configPropertyKey) {
 
-    if (!propertyValue) {
-      return defaultPort;
-    }
+    var defaultPort = Em.get(portConfigs, protocol+'_default_port');
+    var portProperty = Em.get(portConfigs,  protocol+'_property');
+    var site = configProperties.findProperty('type', Em.get(portConfigs, 'site'));
+    var propertyValue = site && site.properties && site.properties[portProperty];
 
-    var re = new RegExp(item.get('regex'));
-    var portValue = propertyValue.match(re);
+    if (!propertyValue)
+      return defaultPort;
 
-    return portValue[1];
+    var regexValue = Em.get(portConfigs, 'regex');
+    regexValue = regexValue.trim();
+    if(regexValue){
+      var re = new RegExp(regexValue);
+      var portValue = propertyValue.match(re);
+      try {
+        return portValue[1];
+      }catch(err) {
+        return defaultPort;
+      }
+    } else {
+      return propertyValue;
+    }
   }
 });

+ 313 - 123
ambari-web/test/views/common/quick_link_view_test.js

@@ -53,15 +53,15 @@ describe('App.QuickViewLinks', function () {
   describe("#didInsertElement()", function () {
     beforeEach(function () {
       sinon.stub(App.router, 'get').returns({p: 1});
-      sinon.stub(quickViewLinks, 'setQuickLinks');
+      sinon.stub(quickViewLinks, 'loadQuickLinksConfigurations');
     });
     afterEach(function () {
       App.router.get.restore();
-      quickViewLinks.setQuickLinks.restore();
+      quickViewLinks.loadQuickLinksConfigurations.restore();
     });
     it("", function () {
       quickViewLinks.didInsertElement();
-      expect(quickViewLinks.setQuickLinks.calledOnce).to.be.true;
+      expect(quickViewLinks.loadQuickLinksConfigurations.calledOnce).to.be.true;
     });
   });
 
@@ -132,6 +132,7 @@ describe('App.QuickViewLinks', function () {
     afterEach(function () {
       quickViewLinks.setConfigProperties.restore();
       quickViewLinks.getQuickLinksHosts.restore();
+
     });
     it("", function () {
       var data = {
@@ -160,7 +161,7 @@ describe('App.QuickViewLinks', function () {
     afterEach(function () {
       quickViewLinks.getQuickLinksHosts.restore();
     });
-    it("call getQuickLinksHosts", function () {
+    it("call loadQuickLinksConfigurations", function () {
       quickViewLinks.loadTagsError();
       expect(quickViewLinks.getQuickLinksHosts.calledOnce).to.be.true;
     });
@@ -228,11 +229,10 @@ describe('App.QuickViewLinks', function () {
       quickViewLinks.setQuickLinksSuccessCallback();
       expect(quickViewLinks.setEmptyLinks.calledOnce).to.be.true;
     });
-    it("quickLinks is null", function () {
+    it("quickLinks is not configured", function () {
       this.mock.returns([{}]);
-      quickViewLinks.set('content.quickLinks', null);
       quickViewLinks.setQuickLinksSuccessCallback();
-      expect(quickViewLinks.setEmptyLinks.calledOnce).to.be.true;
+      expect(quickViewLinks.setEmptyLinks.calledOnce).to.be.false;
     });
     it("single host", function () {
       this.mock.returns([{hostName: 'host1'}]);
@@ -275,6 +275,7 @@ describe('App.QuickViewLinks', function () {
     });
     it("", function () {
       quickViewLinks.set('actualTags', [{siteName: 'hdfs-site'}]);
+      quickViewLinks.set('requiredSiteNames', ['hdfs-site']);
       quickViewLinks.setConfigProperties();
       expect(mock.getConfigsByTags.calledWith([{siteName: 'hdfs-site'}])).to.be.true;
     });
@@ -463,73 +464,305 @@ describe('App.QuickViewLinks', function () {
 
   describe('#setProtocol', function () {
     var tests = [
+      //Yarn
       {
-        serviceName: "YARN", configProperties: [
-        {type: 'yarn-site', properties: {'yarn.http.policy': 'HTTPS_ONLY'}}
-      ], m: "https for yarn", result: "https"
+        serviceName: "YARN",
+        configProperties: [
+          {type: 'yarn-site', properties: {'yarn.http.policy': 'HTTPS_ONLY'}}
+        ],
+        quickLinksConfig: {
+          protocol:{
+            type:"https",
+            checks:[
+                     {property:"yarn.http.policy",
+                      desired:"HTTPS_ONLY",
+                      site:"yarn-site"}
+            ]
+          }
+        },
+        m: "https for yarn (checks for https passed)",
+        result: "https"
       },
       {
-        serviceName: "YARN", configProperties: [
-        {type: 'yarn-site', properties: {'yarn.http.policy': 'HTTP_ONLY'}}
-      ], m: "http for yarn", result: "http"
+        serviceName: "YARN",
+        configProperties: [
+          {type: 'yarn-site', properties: {'yarn.http.policy': 'HTTP_ONLY'}}
+        ],
+        quickLinksConfig: {
+          protocol:{
+            type:"http",
+            checks:[
+                     {property:"yarn.http.policy",
+                      desired:"HTTP_ONLY",
+                      site:"yarn-site"}
+            ]
+          }
+        },
+        m: "http for yarn (checks for http passed)",
+        result: "http"
       },
       {
-        serviceName: "YARN", configProperties: [
-        {type: 'yarn-site', properties: {'yarn.http.policy': 'HTTP_ONLY'}}
-      ], m: "http for yarn (overrides hadoop.ssl.enabled)", result: "http"
+        serviceName: "YARN",
+        configProperties: [
+          {type: 'yarn-site', properties: {'yarn.http.policy': 'HTTP_ONLY'}}
+        ],
+        quickLinksConfig: {
+          protocol:{
+            type:"https",
+            checks:[
+                     {property:"yarn.http.policy",
+                      desired:"HTTPS_ONLY",
+                      site:"yarn-site"}
+            ]
+          }
+        },
+        m: "http for yarn (checks for https did not pass)",
+        result: "http"
       },
       {
-        serviceName: "YARN", configProperties: [
-        {type: 'yarn-site', properties: {'yarn.http.policy': 'HTTPS_ONLY'}}
-      ], m: "https for yarn (overrides hadoop.ssl.enabled)", result: "https"
+        serviceName: "YARN",
+        configProperties: [
+          {type: 'yarn-site', properties: {'yarn.http.policy': 'HTTPS_ONLY'}}
+        ],
+        quickLinksConfig: {
+          protocol:{
+            type:"http",
+            checks:[
+                     {property:"yarn.http.policy",
+                      desired:"HTTP_ONLY",
+                      site:"yarn-site"}
+            ]
+          }
+        },
+        m: "https for yarn (checks for http did not pass)",
+        result: "https"
       },
       {
-        serviceName: "MAPREDUCE2", configProperties: [
-        {type: 'mapred-site', properties: {'mapreduce.jobhistory.http.policy': 'HTTPS_ONLY'}}
-      ], m: "https for mapreduce2", result: "https"
+        serviceName: "YARN",
+        configProperties: [
+          {type: 'yarn-site', properties: {'yarn.http.policy': 'HTTP_ONLY'}}
+        ],
+        quickLinksConfig: {
+          protocol:{
+            type:"HTTP_ONLY",
+            checks:[
+                     {property:"yarn.http.policy",
+                      desired:"HTTPS_ONLY",
+                      site:"yarn-site"}
+            ]
+          }
+        },
+        m: "http for yarn (override checks with specific protocol type)",
+        result: "http"
       },
       {
-        serviceName: "MAPREDUCE2", configProperties: [
-        {type: 'mapred-site', properties: {'mapreduce.jobhistory.http.policy': 'HTTP_ONLY'}}
-      ], m: "http for mapreduce2", result: "http"
+        serviceName: "YARN",
+        configProperties: [
+          {type: 'yarn-site', properties: {'yarn.http.policy': 'HTTPS_ONLY'}}
+        ],
+        quickLinksConfig: {
+          protocol:{
+            type:"HTTPS_ONLY",
+            checks:[
+                     {property:"yarn.http.policy",
+                      desired:"HTTPS_ONLY",
+                      site:"yarn-site"}
+            ]
+          }
+        },
+        m: "https for yarn (override checks with specific protocol type)",
+        result: "https"
       },
+      //Any service - override hadoop.ssl.enabled
       {
-        serviceName: "ANYSERVICE", configProperties: [
-        {type: 'hdfs-site', properties: {'dfs.http.policy': 'HTTPS_ONLY'}}
-      ], m: "https for anyservice", servicesSupportsHttps: ["ANYSERVICE"], result: "https"
+        serviceName: "MyService",
+        configProperties: [
+          {type: 'myservice-site', properties: {'myservice.http.policy': 'HTTPS_ONLY'}},
+          {type: 'hdfs-site', properties: {'dfs.http.policy':'HTTP_ONLY'}}
+        ],
+        quickLinksConfig: {
+          protocol:{
+            type:"https",
+            checks:[
+                     {property:"myservice.http.policy",
+                      desired:"HTTPS_ONLY",
+                      site:"myservice-site"}
+            ]
+          }
+        },
+        m: "https for MyService (checks for https passed, override hadoop.ssl.enabled)",
+        result: "https"
       },
+      //Oozie
       {
-        serviceName: "RANGER", configProperties: [
-        {type: 'ranger-site', properties: {'http.enabled': 'true'}}
-      ], m: "http for ranger (HDP2.2)", result: "http"
+        serviceName: "OOZIE",
+        configProperties: [
+          {type: 'oozie-site', properties: {'oozie.https.port': '12345', 'oozie.https.keystore.file':'/tmp/oozie.jks', 'oozie.https.keystore.pass':'mypass'}}
+        ],
+        quickLinksConfig: {
+          protocol:{
+            type:"https",
+            checks:
+            [
+              {
+                "property":"oozie.https.port",
+                "desired":"EXIST",
+                "site":"oozie-site"
+              },
+              {
+                "property":"oozie.https.keystore.file",
+                "desired":"EXIST",
+                "site":"oozie-site"
+              },
+              {
+                "property":"oozie.https.keystore.pass",
+                "desired":"EXIST",
+                "site":"oozie-site"
+              }
+            ]
+          }
+        },
+        m: "https for oozie (checks for https passed)",
+        result: "https"
       },
       {
-        serviceName: "RANGER", configProperties: [
-        {type: 'ranger-site', properties: {'http.enabled': 'false'}}
-      ], m: "https for ranger (HDP2.2)", result: "https"
+        serviceName: "OOZIE",
+        configProperties: [
+          {type: 'oozie-site', properties: {"oozie.base.url":"http://c6401.ambari.apache.org:11000/oozie"}}
+        ],
+        quickLinksConfig: {
+          protocol:{
+            type:"https",
+            checks:
+            [
+              {
+                "property":"oozie.https.port",
+                "desired":"EXIST",
+                "site":"oozie-site"
+              },
+              {
+                "property":"oozie.https.keystore.file",
+                "desired":"EXIST",
+                "site":"oozie-site"
+              },
+              {
+                "property":"oozie.https.keystore.pass",
+                "desired":"EXIST",
+                "site":"oozie-site"
+              }
+            ]
+          }
+        },
+        m: "http for oozie (checks for https did not pass)",
+        result: "http"
       },
+      //Ranger: HDP 2.2
       {
-        serviceName: "RANGER", configProperties: [
-        {
-          type: 'ranger-admin-site',
-          properties: {'ranger.service.http.enabled': 'true', 'ranger.service.https.attrib.ssl.enabled': 'false'}
-        }
-      ], m: "http for ranger (HDP2.3)", result: "http"
+        serviceName: "RANGER",
+        configProperties: [{type: 'ranger-site', properties: {'http.enabled': 'false'}}],
+        quickLinksConfig: {
+          protocol:{
+            type:"https",
+            checks:
+            [
+              {
+                "property":"http.enabled",
+                "desired":"false",
+                "site":"ranger-site"
+              }
+            ]
+          }
+        },
+        m: "https for ranger (HDP2.2, checks passed)",
+        result: "https"
       },
       {
-        serviceName: "RANGER", configProperties: [
-        {
-          type: 'ranger-admin-site',
-          properties: {'ranger.service.http.enabled': 'false', 'ranger.service.https.attrib.ssl.enabled': 'true'}
-        }
-      ], m: "https for ranger (HDP2.3)", result: "https"
+        serviceName: "RANGER",
+        configProperties: [{type: 'ranger-site', properties: {'http.enabled': 'true'}}],
+        quickLinksConfig: {
+          protocol:{
+            type:"https",
+            checks:
+            [
+              {
+                "property":"http.enabled",
+                "desired":"false",
+                "site":"ranger-site"
+              }
+            ]
+          }
+        },
+        m: "http for ranger (HDP2.2, checks for https did not pass)",
+        result: "http"
+      },
+      //Ranger: HDP 2.3
+      {
+        serviceName: "RANGER",
+        configProperties:
+        [
+          {
+            type: 'ranger-admin-site',
+            properties: {'ranger.service.http.enabled': 'false', 'ranger.service.https.attrib.ssl.enabled': 'true'}
+          },
+        ],
+        quickLinksConfig: {
+          protocol:{
+            type:"https",
+            checks:
+            [
+              {
+                "property":"ranger.service.http.enabled",
+                "desired":"false",
+                "site":"ranger-admin-site"
+              },
+              {
+                "property":"ranger.service.https.attrib.ssl.enabled",
+                "desired":"true",
+                "site":"ranger-admin-site"
+              }
+            ]
+          }
+        },
+
+        m: "https for ranger (HDP2.3, checks passed)",
+        result: "https"
+      },
+      {
+        serviceName: "RANGER",
+        configProperties:
+        [
+          {
+            type: 'ranger-admin-site',
+            properties: {'ranger.service.http.enabled': 'true', 'ranger.service.https.attrib.ssl.enabled': 'false'}
+          },
+        ],
+        quickLinksConfig: {
+          protocol:{
+            type:"https",
+            checks:
+            [
+              {
+                "property":"ranger.service.http.enabled",
+                "desired":"false",
+                "site":"ranger-admin-site"
+              },
+              {
+                "property":"ranger.service.https.attrib.ssl.enabled",
+                "desired":"true",
+                "site":"ranger-admin-site"
+              }
+            ]
+          }
+        },
+        m: "http for ranger (HDP2.3, checks for https did not pass)",
+        result: "http"
       }
     ];
 
     tests.forEach(function (t) {
       it(t.m, function () {
         quickViewLinks.set('servicesSupportsHttps', t.servicesSupportsHttps);
-        expect(quickViewLinks.setProtocol(t.serviceName, t.configProperties, t.ambariProperties)).to.equal(t.result);
+        expect(quickViewLinks.setProtocol(t.configProperties, t.quickLinksConfig)).to.equal(t.result);
       });
     });
   });
@@ -537,86 +770,43 @@ describe('App.QuickViewLinks', function () {
   describe('#setPort', function () {
     var testData = [
       Em.Object.create({
-        'service_id': 'YARN',
         'protocol': 'http',
-        'result': '8088',
-        'default_http_port': '8088',
-        'default_https_port': '8090',
-        'regex': '\\w*:(\\d+)'
-      }),
-      Em.Object.create({
-        'service_id': 'YARN',
-        'protocol': 'https',
-        'https_config': 'https_config',
-        'result': '8090',
-        'default_http_port': '8088',
-        'default_https_port': '8090',
-        'regex': '\\w*:(\\d+)'
+        'port':{
+          'http_property':'yarn.timeline-service.webapp.address',
+          'http_default_port':'8188',
+          'https_property':'yarn.timeline-service.webapp.https.address',
+          'https_default_port':'8090',
+          'regex': '\\w*:(\\d+)',
+          'site':'yarn-site'
+        },
+        'configProperties':
+        [
+          {
+            'type': 'yarn-site',
+            'properties': {'yarn.timeline-service.webapp.address': 'c6401.ambari.apache.org:8188'}
+          },
+        ],
+        'result': '8188',
       }),
+
       Em.Object.create({
-        'service_id': 'YARN',
         'protocol': 'https',
-        'https_config': 'https_config',
+        'port':{
+          'http_property':'yarn.timeline-service.webapp.address',
+          'http_default_port':'8188',
+          'https_property':'yarn.timeline-service.webapp.https.address',
+          'https_default_port':'8090',
+          'regex': '\\w*:(\\d+)',
+          'site':'yarn-site'
+        },
+        'configProperties':
+        [
+          {
+            'type': 'yarn-site',
+            'properties': {'yarn.timeline-service.webapp.https.address': 'c6401.ambari.apache.org:8090'}
+          },
+        ],
         'result': '8090',
-        'default_http_port': '8088',
-        'default_https_port': '8090',
-        'regex': '\\w*:(\\d+)'
-      }),
-      Em.Object.create({
-        'service_id': 'YARN',
-        'protocol': 'https',
-        'https_config': 'https_config',
-        'config': 'https_config_custom',
-        'site': 'yarn-site',
-        'result': '9091',
-        'default_http_port': '8088',
-        'default_https_port': '8090',
-        'regex': '\\w*:(\\d+)',
-        'configProperties': [{
-          'type': 'yarn-site',
-          'properties': {
-            'https_config': 'h:9090',
-            'https_config_custom': 'h:9091'
-          }
-        }]
-      }),
-      Em.Object.create({
-        'service_id': 'YARN',
-        'protocol': 'https',
-        'http_config': 'http_config',
-        'https_config': 'https_config',
-        'site': 'yarn-site',
-        'result': '9090',
-        'default_http_port': '8088',
-        'default_https_port': '8090',
-        'regex': '\\w*:(\\d+)',
-        'configProperties': [{
-          'type': 'yarn-site',
-          'properties': {
-            'http_config': 'h:9088',
-            'https_config': 'h:9090'
-          }
-        }]
-      }),
-      Em.Object.create({
-        'service_id': 'RANGER',
-        'protocol': 'http',
-        'http_config': 'http_config',
-        'https_config': 'https_config',
-        'result': '6080',
-        'default_http_port': '6080',
-        'default_https_port': '6182',
-        'regex': '(\\d*)+'
-      }),
-      Em.Object.create({
-        'service_id': 'RANGER',
-        'protocol': 'https',
-        'http_config': 'http_config',
-        'https_config': 'https_config',
-        'result': '6182',
-        'default_http_port': '6080',
-        'default_https_port': '6182',
-        'regex': '(\\d*)+'
       })
     ];
 
@@ -627,7 +817,7 @@ describe('App.QuickViewLinks', function () {
     testData.forEach(function (item) {
       it(item.service_id + ' ' + item.protocol, function () {
         quickViewLinks.set('configProperties', item.configProperties || []);
-        expect(quickViewLinks.setPort(item, item.protocol, item.config)).to.equal(item.result);
+        expect(quickViewLinks.setPort(item.port, item.protocol, item.configProperties)).to.equal(item.result);
       })
     }, this);
   });