ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From stoa...@apache.org
Subject ambari git commit: AMBARI-19597. Blueprint installation should accept quick link profile. (Balazs Bence Sari via stoader)
Date Thu, 19 Jan 2017 20:34:52 GMT
Repository: ambari
Updated Branches:
  refs/heads/trunk 504b45528 -> de8bf6019


AMBARI-19597. Blueprint installation should accept quick link profile. (Balazs Bence Sari via stoader)


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

Branch: refs/heads/trunk
Commit: de8bf601918efbeb66ef68d25364aaed4b672172
Parents: 504b455
Author: Balazs Bence Sari <bsari@hortonworks.com>
Authored: Thu Jan 19 20:56:02 2017 +0100
Committer: Toader, Sebastian <stoader@hortonworks.com>
Committed: Thu Jan 19 20:56:02 2017 +0100

----------------------------------------------------------------------
 .../AmbariManagementControllerImpl.java         |  10 +-
 .../internal/ClusterResourceProvider.java       |  17 +-
 .../internal/ProvisionClusterRequest.java       |  36 +++
 .../quicklinksprofile/AcceptAllFilter.java      |   5 +
 .../state/quicklinksprofile/Component.java      |   3 +
 .../server/state/quicklinksprofile/Filter.java  |   7 +-
 .../quicklinksprofile/QuickLinksProfile.java    |   9 +
 .../QuickLinksProfileBuilder.java               | 128 ++++++++++
 .../QuickLinksProfileEvaluationException.java   |   4 +
 .../QuickLinksProfileParser.java                |   6 +-
 .../server/state/quicklinksprofile/Service.java |   3 +
 .../ambari/server/topology/TopologyManager.java |  38 +++
 .../internal/ProvisionClusterRequestTest.java   |  66 ++++++
 .../QuickLinkArtifactResourceProviderTest.java  |   1 -
 .../QuickLinksProfileBuilderTest.java           | 234 +++++++++++++++++++
 .../server/topology/TopologyManagerTest.java    |  88 ++++++-
 16 files changed, 632 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index 5e8c803..9afe598 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -185,6 +185,7 @@ import org.apache.ambari.server.state.State;
 import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
 import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityController;
 import org.apache.ambari.server.state.quicklinksprofile.QuickLinkVisibilityControllerFactory;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfile;
 import org.apache.ambari.server.state.repository.VersionDefinitionXml;
 import org.apache.ambari.server.state.scheduler.RequestExecutionFactory;
 import org.apache.ambari.server.state.stack.RepositoryXml;
@@ -239,12 +240,6 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
   public static final String SKIP_INSTALL_FOR_COMPONENTS = "skipInstallForComponents";
   public static final String DONT_SKIP_INSTALL_FOR_COMPONENTS = "dontSkipInstallForComponents";
 
-  /**
-   * The name of the ambari setting that stores the quicklinks profile.
-   * See {@link org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfile}
-   */
-  public static final String SETTING_QUICKLINKS_PROFILE = "QuickLinksProfile";
-
   private final Clusters clusters;
 
   private final ActionManager actionManager;
@@ -511,7 +506,6 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
         c.createClusterVersion(stackId, versionEntity.getVersion(), getAuthName(), RepositoryVersionState.INIT);
       }
     }
-
   }
 
   @Override
@@ -5527,7 +5521,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
 
   @Override
   public QuickLinkVisibilityController getQuicklinkVisibilityController() {
-    SettingEntity entity = settingDAO.findByName(SETTING_QUICKLINKS_PROFILE);
+    SettingEntity entity = settingDAO.findByName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE);
     String quickLinkProfileJson = null != entity ? entity.getContent() : null;
     return QuickLinkVisibilityControllerFactory.get(quickLinkProfileJson);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
index cb30f2d..613ab3f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
@@ -82,6 +82,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
   public static final String BLUEPRINT_PROPERTY_ID = PropertyHelper.getPropertyId(null, "blueprint");
   public static final String SECURITY_PROPERTY_ID = PropertyHelper.getPropertyId(null, "security");
   public static final String CREDENTIALS_PROPERTY_ID = PropertyHelper.getPropertyId(null, "credentials");
+  public static final String QUICKLINKS_PROFILE_PROPERTY_ID = PropertyHelper.getPropertyId(null, "quicklinks_profile");
   public static final String SESSION_ATTRIBUTES_PROPERTY_ID = "session_attributes";
 
   public static final String CLUSTER_REPO_VERSION = "Clusters/repository_version";
@@ -152,6 +153,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
     propertyIds.add(SECURITY_PROPERTY_ID);
     propertyIds.add(CREDENTIALS_PROPERTY_ID);
     propertyIds.add(CLUSTER_REPO_VERSION);
+    propertyIds.add(QUICKLINKS_PROFILE_PROPERTY_ID);
   }
 
 
@@ -356,13 +358,13 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
 
     for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
       final ClusterRequest clusterRequest = getRequest(propertyMap);
-        modifyResources(new Command<Void>() {
-          @Override
-          public Void invoke() throws AmbariException {
-            getManagementController().deleteCluster(clusterRequest);
-            return null;
-          }
-        });
+      modifyResources(new Command<Void>() {
+        @Override
+        public Void invoke() throws AmbariException {
+          getManagementController().deleteCluster(clusterRequest);
+          return null;
+        }
+      });
     }
     notifyDelete(Resource.Type.Cluster, predicate);
     return getRequestStatus(null);
@@ -436,6 +438,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider
     return cr;
   }
 
+
   /**
    * Get the map of session attributes from the given property map.
    *

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
index a35da86..2c8d09a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java
@@ -27,6 +27,8 @@ import java.util.Set;
 import org.apache.ambari.server.api.predicate.InvalidQueryException;
 import org.apache.ambari.server.security.encryption.CredentialStoreType;
 import org.apache.ambari.server.stack.NoSuchStackException;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileBuilder;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileEvaluationException;
 import org.apache.ambari.server.topology.ConfigRecommendationStrategy;
 import org.apache.ambari.server.topology.Configuration;
 import org.apache.ambari.server.topology.ConfigurationFactory;
@@ -105,6 +107,17 @@ public class ProvisionClusterRequest extends BaseClusterRequest {
    */
   public static final String REPO_VERSION_PROPERTY = "repository_version";
 
+  /**
+   * The global quick link filters property
+   */
+  public static final String QUICKLINKS_PROFILE_FILTERS_PROPERTY = "quicklinks_profile/filters";
+
+  /**
+   * The service and component level quick link filters property
+   */
+  public static final String QUICKLINKS_PROFILE_SERVICES_PROPERTY = "quicklinks_profile/services";
+
+
 
   /**
    * configuration factory
@@ -130,6 +143,8 @@ public class ProvisionClusterRequest extends BaseClusterRequest {
 
   private String repoVersion;
 
+  private final String quickLinksProfileJson;
+
   private final static Logger LOG = LoggerFactory.getLogger(ProvisionClusterRequest.class);
 
   /**
@@ -173,8 +188,23 @@ public class ProvisionClusterRequest extends BaseClusterRequest {
     this.configRecommendationStrategy = parseConfigRecommendationStrategy(properties);
 
     setProvisionAction(parseProvisionAction(properties));
+
+    try {
+      this.quickLinksProfileJson = processQuickLinksProfile(properties);
+    }
+    catch (QuickLinksProfileEvaluationException ex) {
+      throw new InvalidTopologyTemplateException("Invalid quick links profile", ex);
+    }
+  }
+
+  private String processQuickLinksProfile(Map<String, Object> properties) throws QuickLinksProfileEvaluationException {
+    Object globalFilters = properties.get(QUICKLINKS_PROFILE_FILTERS_PROPERTY);
+    Object serviceFilters = properties.get(QUICKLINKS_PROFILE_SERVICES_PROPERTY);
+    return (null != globalFilters || null != serviceFilters) ?
+      new QuickLinksProfileBuilder().buildQuickLinksProfile(globalFilters, serviceFilters) : null;
   }
 
+
   private Map<String, Credential> parseCredentials(Map<String, Object> properties) throws
     InvalidTopologyTemplateException {
     HashMap<String, Credential> credentialHashMap = new HashMap<>();
@@ -439,4 +469,10 @@ public class ProvisionClusterRequest extends BaseClusterRequest {
     return repoVersion;
   }
 
+  /**
+   * @return the quick links profile in Json string format
+   */
+  public String getQuickLinksProfileJson() {
+    return quickLinksProfileJson;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
index d784a22..069ae3f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/AcceptAllFilter.java
@@ -43,4 +43,9 @@ public class AcceptAllFilter extends Filter {
   public int hashCode() {
     return java.util.Objects.hash(isVisible());
   }
+
+  @Override
+  public String toString() {
+    return getClass().getSimpleName() + " (visible=" + isVisible() + ")";
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
index a1267df..729e5d4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Component.java
@@ -24,6 +24,8 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 
+import com.google.common.base.Preconditions;
+
 /**
  * Class to represent component-level filter definitions
  */
@@ -37,6 +39,7 @@ public class Component {
   private List<Filter> filters;
 
   static Component create(String name, List<Filter> filters) {
+    Preconditions.checkNotNull(name, "Component name must not be null");
     Component component = new Component();
     component.setName(name);
     component.setFilters(filters);

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
index c551830..a62fee0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Filter.java
@@ -23,6 +23,8 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 
+import com.google.common.base.Preconditions;
+
 @JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
 @JsonIgnoreProperties(ignoreUnknown = true)
 /**
@@ -33,8 +35,9 @@ import org.codehaus.jackson.map.annotate.JsonSerialize;
  * </ul>
  */
 public abstract class Filter {
+  static final String VISIBLE = "visible";
 
-  @JsonProperty("visible")
+  @JsonProperty(VISIBLE)
   private boolean visible;
 
   /**
@@ -63,6 +66,7 @@ public abstract class Filter {
   }
 
   static LinkNameFilter linkNameFilter(String linkName, boolean visible) {
+    Preconditions.checkNotNull(linkName, "Link name must not be null");
     LinkNameFilter linkNameFilter = new LinkNameFilter();
     linkNameFilter.setLinkName(linkName);
     linkNameFilter.setVisible(visible);
@@ -70,6 +74,7 @@ public abstract class Filter {
   }
 
   static LinkAttributeFilter linkAttributeFilter(String linkAttribute, boolean visible) {
+    Preconditions.checkNotNull(linkAttribute, "Attribute name must not be null");
     LinkAttributeFilter linkAttributeFilter = new LinkAttributeFilter();
     linkAttributeFilter.setLinkAttribute(linkAttribute);
     linkAttributeFilter.setVisible(visible);

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
index c9ac6b4..7d480e9 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfile.java
@@ -41,6 +41,15 @@ import org.codehaus.jackson.map.annotate.JsonSerialize;
 @JsonIgnoreProperties(ignoreUnknown = true)
 public class QuickLinksProfile {
 
+  /**
+   * The name of the Ambari setting that stores the quick links profile
+   */
+  public static final String SETTING_NAME_QUICKLINKS_PROFILE = "QuickLinksProfile";
+  /**
+   * The type of the Ambari setting that stores the quick links profile
+   */
+  public static final String SETTING_TYPE_AMBARI_SERVER = "ambari-server";
+
   @JsonProperty("filters")
   private List<Filter> filters;
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.java
new file mode 100644
index 0000000..fca1155
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilder.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.state.quicklinksprofile;
+
+import static org.apache.ambari.server.state.quicklinksprofile.Filter.VISIBLE;
+import static org.apache.ambari.server.state.quicklinksprofile.LinkAttributeFilter.LINK_ATTRIBUTE;
+import static org.apache.ambari.server.state.quicklinksprofile.LinkNameFilter.LINK_NAME;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Class to create a {@link QuickLinksProfile} based on data received in a request
+ */
+public class QuickLinksProfileBuilder {
+
+  public static final String NAME = "name";
+  public static final String COMPONENTS = "components";
+  public static final String FILTERS = "filters";
+
+  /**
+   *
+   * @param globalFiltersRaw The data in the request belonging to the "quicklinks_profile/filters" key.
+   * @param serviceFiltersRaw The data in the request belonging to "quicklinks_profile/services"
+   * @return The quicklinks profile as Json encoded string.
+   * @throws QuickLinksProfileEvaluationException when the received data defines an invalid profile. This can be of various
+   *    reasons: the received data is has invalid structure, critical data is missing or there are contradicting filter
+   *    rules in the profile
+   */
+  public String buildQuickLinksProfile(@Nullable Object globalFiltersRaw, @Nullable Object serviceFiltersRaw) throws QuickLinksProfileEvaluationException {
+    try {
+      List<Filter> globalFilters = buildQuickLinkFilters(globalFiltersRaw);
+      List<Service> services = buildServices(serviceFiltersRaw);
+      QuickLinksProfile profile = QuickLinksProfile.create(globalFilters, services);
+      // sanity check: this should throw QuickLinksProfileEvaluationException if the profile is invalid
+      new DefaultQuickLinkVisibilityController(profile);
+      return new QuickLinksProfileParser().encode(profile);
+    }
+    catch (QuickLinksProfileEvaluationException ex) {
+      throw ex;
+    }
+    catch (Exception ex) {
+      throw new QuickLinksProfileEvaluationException("Error interpreting quicklinks profile data", ex);
+    }
+  }
+
+  List<Service> buildServices(@Nullable Object servicesRaw) {
+    if (null == servicesRaw) {
+      return ImmutableList.of();
+    }
+    List<Service> services = new ArrayList<>();
+    for (Map<String, Object> serviceAsMap: (Collection<Map<String, Object>>)servicesRaw) {
+      String serviceName = (String)serviceAsMap.get(NAME);
+      Object componentsRaw = serviceAsMap.get(COMPONENTS);
+      Object filtersRaw = serviceAsMap.get(FILTERS);
+      services.add(Service.create(serviceName,
+          buildQuickLinkFilters(filtersRaw),
+          buildComponents(componentsRaw)));
+    }
+    return services;
+  }
+
+  List<Component> buildComponents(@Nullable Object componentsRaw) {
+    if (null == componentsRaw) {
+      return ImmutableList.of();
+    }
+    List<Component> components = new ArrayList<>();
+    for (Map<String, Object> componentAsMap: (Collection<Map<String, Object>>)componentsRaw) {
+      String componentName = (String)componentAsMap.get(NAME);
+      Object filtersRaw = componentAsMap.get(FILTERS);
+      components.add(Component.create(componentName,
+          buildQuickLinkFilters(filtersRaw)));
+    }
+    return components;  }
+
+
+  List<Filter> buildQuickLinkFilters(@Nullable Object filtersRaw) throws ClassCastException, IllegalArgumentException {
+    if (null == filtersRaw) {
+      return ImmutableList.of();
+    }
+    List<Filter> filters  = new ArrayList<>();
+    for (Map<String, String> filterAsMap: (Collection<Map<String, String>>)filtersRaw) {
+      String linkName = filterAsMap.get(LINK_NAME);
+      String attributeName = filterAsMap.get(LINK_ATTRIBUTE);
+      boolean visible = Boolean.parseBoolean(filterAsMap.get(VISIBLE));
+
+      if (null != linkName && null != attributeName) {
+        throw new IllegalArgumentException(
+            String.format("%s link_name: %s, link_attribute: %s",
+                QuickLinksFilterDeserializer.PARSE_ERROR_MESSAGE, linkName, attributeName));
+      }
+      else if (null != linkName) {
+        filters.add(Filter.linkNameFilter(linkName, visible));
+      }
+      else if (null != attributeName) {
+        filters.add(Filter.linkAttributeFilter(attributeName, visible));
+      }
+      else {
+        filters.add(Filter.acceptAllFilter(visible));
+      }
+    }
+    return filters;
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java
index 26819e1..1ef8a59 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileEvaluationException.java
@@ -28,4 +28,8 @@ public class QuickLinksProfileEvaluationException extends Exception {
     super(message);
   }
 
+  public QuickLinksProfileEvaluationException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
index a3ae677..150b7d4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileParser.java
@@ -55,13 +55,17 @@ public class QuickLinksProfileParser {
   public QuickLinksProfile parse(URL url) throws IOException {
     return parse(Resources.toByteArray(url));
   }
+
+  public String encode(QuickLinksProfile profile) throws IOException {
+    return mapper.writeValueAsString(profile);
+  }
 }
 
 /**
  * Custom deserializer is needed to handle filter polymorphism.
  */
 class QuickLinksFilterDeserializer extends StdDeserializer<Filter> {
-  private static final String PARSE_ERROR_MESSAGE =
+  static final String PARSE_ERROR_MESSAGE =
       "A filter is not allowed to declare both link_name and link_attribute at the same time.";
 
   QuickLinksFilterDeserializer() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
index 7724852..07cce29 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/quicklinksprofile/Service.java
@@ -24,6 +24,8 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 
+import com.google.common.base.Preconditions;
+
 /**
  * Class to represent component-level filter definitions
  */
@@ -40,6 +42,7 @@ public class Service {
   private List<Filter> filters;
 
   static Service create(String name, List<Filter> filters, List<Component> components) {
+    Preconditions.checkNotNull(name, "Service name must not be null");
     Service service = new Service();
     service.setName(name);
     service.setFilters(filters);

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyManager.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyManager.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyManager.java
index 7db07a0..3103c34 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyManager.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyManager.java
@@ -63,10 +63,14 @@ import org.apache.ambari.server.events.HostsRemovedEvent;
 import org.apache.ambari.server.events.RequestFinishedEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.orm.dao.HostRoleCommandStatusSummaryDTO;
+import org.apache.ambari.server.orm.dao.SettingDAO;
+import org.apache.ambari.server.orm.entities.SettingEntity;
 import org.apache.ambari.server.orm.entities.StageEntity;
+import org.apache.ambari.server.security.authorization.AuthorizationHelper;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.SecurityType;
 import org.apache.ambari.server.state.host.HostImpl;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfile;
 import org.apache.ambari.server.utils.RetryHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -118,6 +122,9 @@ public class TopologyManager {
   @Inject
   private AmbariEventPublisher ambariEventPublisher;
 
+  @Inject
+  private SettingDAO settingDAO;
+
   /**
    * A boolean not cached thread-local (volatile) to prevent double-checked
    * locking on the synchronized keyword.
@@ -232,6 +239,11 @@ public class TopologyManager {
 
   public RequestStatusResponse provisionCluster(final ProvisionClusterRequest request) throws InvalidTopologyException, AmbariException {
     ensureInitialized();
+
+    if (null != request.getQuickLinksProfileJson()) {
+      saveOrUpdateQuickLinksProfile(request.getQuickLinksProfileJson());
+    }
+
     ClusterTopology topology = new ClusterTopologyImpl(ambariContext, request);
     final String clusterName = request.getClusterName();
     final String repoVersion = request.getRepositoryVersion();
@@ -302,6 +314,32 @@ public class TopologyManager {
     return getRequestStatus(logicalRequest.getRequestId());
   }
 
+  /**
+   * Saves the quick links profile to the DB as an Ambari setting. Creates a new setting entity or updates the existing
+   * one.
+   * @param quickLinksProfileJson the quicklinks profile in Json format
+   */
+  void saveOrUpdateQuickLinksProfile(String quickLinksProfileJson) {
+    SettingEntity settingEntity = settingDAO.findByName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE);
+    // create new
+    if (null == settingEntity) {
+      settingEntity = new SettingEntity();
+      settingEntity.setName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE);
+      settingEntity.setSettingType(QuickLinksProfile.SETTING_TYPE_AMBARI_SERVER);
+      settingEntity.setContent(quickLinksProfileJson);
+      settingEntity.setUpdatedBy(AuthorizationHelper.getAuthenticatedName());
+      settingEntity.setUpdateTimestamp(System.currentTimeMillis());
+      settingDAO.create(settingEntity);
+    }
+    // update existing
+    else {
+      settingEntity.setContent(quickLinksProfileJson);
+      settingEntity.setUpdatedBy(AuthorizationHelper.getAuthenticatedName());
+      settingEntity.setUpdateTimestamp(System.currentTimeMillis());
+      settingDAO.merge(settingEntity);
+    }
+  }
+
   private void submitCredential(String clusterName, Credential credential) {
 
     ResourceProvider provider =

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
index 2cf478a..c64da62 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java
@@ -43,6 +43,7 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.ambari.server.controller.spi.ResourceProvider;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileBuilderTest;
 import org.apache.ambari.server.topology.Blueprint;
 import org.apache.ambari.server.topology.BlueprintFactory;
 import org.apache.ambari.server.topology.Configuration;
@@ -55,6 +56,8 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.google.common.collect.Sets;
+
 /**
  * Unit tests for ProvisionClusterRequest.
  */
@@ -428,6 +431,69 @@ public class ProvisionClusterRequestTest {
     new ProvisionClusterRequest(properties, null);
   }
 
+  @Test
+  public void testQuickLinksProfile_NoDataInRequest() throws Exception {
+    Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
+    ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
+    assertNull("No quick links profile is expected", request.getQuickLinksProfileJson());
+  }
+
+  @Test
+  public void testQuickLinksProfile_OnlyGlobalFilterDataInRequest() throws Exception {
+    Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
+
+    properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_FILTERS_PROPERTY,
+        Sets.newHashSet(QuickLinksProfileBuilderTest.filter(null, null, true)));
+
+    ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
+    assertEquals("Quick links profile doesn't match expected",
+        "{\"filters\":[{\"visible\":true}],\"services\":[]}",
+        request.getQuickLinksProfileJson());
+  }
+
+  @Test
+  public void testQuickLinksProfile_OnlyServiceFilterDataInRequest() throws Exception {
+    Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
+
+    Map<String, String> filter = QuickLinksProfileBuilderTest.filter(null, null, true);
+    Map<String, Object> hdfs = QuickLinksProfileBuilderTest.service("HDFS", null, Sets.newHashSet(filter));
+    Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+    properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_SERVICES_PROPERTY, services);
+
+    ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
+    assertEquals("Quick links profile doesn't match expected",
+        "{\"filters\":[],\"services\":[{\"name\":\"HDFS\",\"components\":[],\"filters\":[{\"visible\":true}]}]}",
+        request.getQuickLinksProfileJson());
+  }
+
+  @Test
+  public void testQuickLinksProfile_BothGlobalAndServiceLevelFilters() throws Exception {
+    Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
+
+    properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_FILTERS_PROPERTY,
+        Sets.newHashSet(QuickLinksProfileBuilderTest.filter(null, null, true)));
+
+    Map<String, String> filter = QuickLinksProfileBuilderTest.filter(null, null, true);
+    Map<String, Object> hdfs = QuickLinksProfileBuilderTest.service("HDFS", null, Sets.newHashSet(filter));
+    Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+    properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_SERVICES_PROPERTY, services);
+
+    ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
+    System.out.println(request.getQuickLinksProfileJson());
+    assertEquals("Quick links profile doesn't match expected",
+        "{\"filters\":[{\"visible\":true}],\"services\":[{\"name\":\"HDFS\",\"components\":[],\"filters\":[{\"visible\":true}]}]}",
+        request.getQuickLinksProfileJson());
+  }
+
+  @Test(expected = InvalidTopologyTemplateException.class)
+  public void testQuickLinksProfile_InvalidRequestData() throws Exception {
+    Map<String, Object> properties = createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
+
+    properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_SERVICES_PROPERTY, "Hello World!");
+
+    ProvisionClusterRequest request = new ProvisionClusterRequest(properties, null);
+  }
+
   public static Map<String, Object> createBlueprintRequestProperties(String clusterName, String blueprintName) {
     Map<String, Object> properties = new LinkedHashMap<String, Object>();
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java
index 8c723c9..689fbe4 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/QuickLinkArtifactResourceProviderTest.java
@@ -192,7 +192,6 @@ public class QuickLinkArtifactResourceProviderTest {
       expect(service.getName()).andReturn("YARN").anyTimes();
       binder.bind(AmbariManagementController.class).toInstance(amc);
       replay(amc, metaInfo, stack, service);
-
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java
new file mode 100644
index 0000000..1cc3fd3
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/quicklinksprofile/QuickLinksProfileBuilderTest.java
@@ -0,0 +1,234 @@
+/*
+ * 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.quicklinksprofile;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileBuilder.COMPONENTS;
+import static org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileBuilder.FILTERS;
+import static org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfileBuilder.NAME;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.junit.Test;
+
+import com.google.common.collect.Sets;
+
+public class QuickLinksProfileBuilderTest {
+
+
+  @Test
+  public void testBuildProfileOnlyGlobalFilters() throws Exception {
+    Set<Map<String, String>> filters = newHashSet(
+      filter("namenode_ui", null, true),
+      filter(null, "sso", true),
+      filter(null, null, false)
+    );
+
+    String profileJson = new QuickLinksProfileBuilder().buildQuickLinksProfile(filters, null);
+
+    //verify
+    QuickLinksProfile profile = new QuickLinksProfileParser().parse(profileJson.getBytes());
+    assertFilterExists(profile, null, null, Filter.linkNameFilter("namenode_ui", true));
+    assertFilterExists(profile, null, null, Filter.linkAttributeFilter("sso", true));
+    assertFilterExists(profile, null, null, Filter.acceptAllFilter(false));
+  }
+
+  @Test
+  public void testBuildProfileOnlyServiceFilters() throws Exception {
+    Map<String, Object> nameNode = component("NAMENODE",
+        newHashSet(filter("namenode_ui", null, false)));
+
+    Map<String, Object> hdfs = service("HDFS",
+        newHashSet(nameNode),
+        newHashSet(filter(null, "sso", true)));
+
+    Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+
+    String profileJson = new QuickLinksProfileBuilder().buildQuickLinksProfile(null, services);
+
+    //verify
+    QuickLinksProfile profile = new QuickLinksProfileParser().parse(profileJson.getBytes());
+    assertFilterExists(profile, "HDFS", "NAMENODE", Filter.linkNameFilter("namenode_ui", false));
+    assertFilterExists(profile, "HDFS", null, Filter.linkAttributeFilter("sso", true));
+  }
+
+  @Test
+  public void testBuildProfileBothGlobalAndServiceFilters() throws Exception {
+    Set<Map<String, String>> globalFilters = newHashSet( filter(null, null, false) );
+
+    Map<String, Object> nameNode = component("NAMENODE",
+        newHashSet(filter("namenode_ui", null, false)));
+
+    Map<String, Object> hdfs = service("HDFS",
+        newHashSet(nameNode),
+        newHashSet(filter(null, "sso", true)));
+
+    Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+
+    String profileJson = new QuickLinksProfileBuilder().buildQuickLinksProfile(globalFilters, services);
+
+    // verify
+    QuickLinksProfile profile = new QuickLinksProfileParser().parse(profileJson.getBytes());
+    assertFilterExists(profile, null, null, Filter.acceptAllFilter(false));
+    assertFilterExists(profile, "HDFS", "NAMENODE", Filter.linkNameFilter("namenode_ui", false));
+    assertFilterExists(profile, "HDFS", null, Filter.linkAttributeFilter("sso", true));
+  }
+
+  @Test(expected = QuickLinksProfileEvaluationException.class)
+  public void testBuildProfileBadInputStructure() throws Exception {
+    new QuickLinksProfileBuilder().buildQuickLinksProfile("Hello", "World");
+  }
+
+  @Test(expected = QuickLinksProfileEvaluationException.class)
+  public void testBuildProfileMissingDataServiceName() throws Exception {
+    Map<String, Object> nameNode = component("NAMENODE",
+        newHashSet(filter("namenode_ui", null, false)));
+
+    Map<String, Object> hdfs = service(null, // intentionally omitting service name
+        newHashSet(nameNode),
+        newHashSet(filter(null, "sso", true)));
+
+    Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+
+    new QuickLinksProfileBuilder().buildQuickLinksProfile(null, services);
+  }
+
+  @Test(expected = QuickLinksProfileEvaluationException.class)
+  public void testBuildProfileMissingDataComponentName() throws Exception {
+    Map<String, Object> nameNode = component(null, // intentionally omitting component name
+        newHashSet(filter("namenode_ui", null, false)));
+
+    Map<String, Object> hdfs = service("HDFS",
+        newHashSet(nameNode),
+        newHashSet(filter(null, "sso", true)));
+
+    Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
+
+    new QuickLinksProfileBuilder().buildQuickLinksProfile(null, services);
+  }
+
+  @Test(expected = QuickLinksProfileEvaluationException.class)
+  public void testBuildProfileInvalidProfileDefiniton() throws Exception {
+    // Contradicting rules in the profile
+    Set<Map<String, String>> filters = newHashSet(
+        filter(null, "sso", true),
+        filter(null, "sso", false)
+    );
+
+    String profileJson = new QuickLinksProfileBuilder().buildQuickLinksProfile(filters, null);
+  }
+
+  /**
+   * Verifies that the filter specified by the arguments exists in the received {@link QuickLinksProfile}
+   * @param profile the {@link QuickLinksProfile} to examine
+   * @param serviceName the service name where the filter is defined. {@code null} means the searched filter is a global
+   *                    filter
+   * @param componentName the component name where the filter is defined. Only makes sense when serviceName is defined
+   *                    too. {@code null} means the searched filter is a service level filter, not component level one.
+   *                    filter
+   * @param filter the {@link Filter} to look for.
+   */
+  private static void assertFilterExists(@Nonnull QuickLinksProfile profile,
+                                         @Nullable String serviceName,
+                                         @Nullable String componentName,
+                                         @Nonnull Filter filter) {
+    // looking for a global filter
+    if (null == serviceName) {
+      if (!profile.getFilters().contains(filter)) {
+        throw new AssertionError("Expected global filter not found: " + filter);
+      }
+    }
+    // looking for a filter defined on service or component level
+    else {
+      Service service = findService(profile.getServices(), serviceName);
+      // looking for a filter defined on service level
+      if (null == componentName) {
+        if (!service.getFilters().contains(filter)) {
+          throw new AssertionError(String.format("Expected filter not found. Service: %s, Filter: %s",
+              serviceName, filter));
+        }
+      }
+      // looking for a filter defined on component level
+      else {
+        Component component = findComponent(service.getComponents(), componentName);
+        if (!component.getFilters().contains(filter)) {
+          throw new AssertionError(String.format("Expected filter not found. Service: %s, Component: %s, Filter: %s",
+              serviceName, componentName, filter));
+        }
+      }
+    }
+  }
+
+  private static Component findComponent(List<Component> components, String componentName) {
+    for (Component component: components) {
+      if (component.getName().equals(componentName)) {
+        return component;
+      }
+    }
+    throw new AssertionError("Expected component not found: " + componentName);
+  }
+
+  private static Service findService(List<Service> services, String serviceName) {
+    for (Service service: services) {
+      if (service.getName().equals(serviceName)) {
+        return service;
+      }
+    }
+    throw new AssertionError("Expected service not found: " + serviceName);
+  }
+
+  public static Map<String, String> filter(@Nullable String linkName, @Nullable String attributeName, boolean visible) {
+    Map<String, String> map = new HashMap<>(3);
+    if (null != linkName) {
+      map.put(LinkNameFilter.LINK_NAME, linkName);
+    }
+    if (null != attributeName) {
+      map.put(LinkAttributeFilter.LINK_ATTRIBUTE, attributeName);
+    }
+    map.put(Filter.VISIBLE, Boolean.toString(visible));
+    return map;
+  }
+
+  public static Map<String, Object> component(String componentName, Set<Map<String, String>> filters) {
+    Map<String, Object> map = new HashMap<>();
+    map.put(NAME, componentName);
+    map.put(FILTERS, filters);
+    return map;
+  }
+
+  public static Map<String, Object> service(String serviceName, Set<Map<String, Object>> components,
+                                             Set<Map<String, String>> filters) {
+    Map<String, Object> map = new HashMap<>();
+    map.put(NAME, serviceName);
+    if (null != components) {
+      map.put(COMPONENTS, components);
+    }
+    if (null != filters) {
+      map.put(FILTERS, filters);
+    }
+    return map;
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/de8bf601/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyManagerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyManagerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyManagerTest.java
index 7e6e5a3..07060b7 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyManagerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyManagerTest.java
@@ -29,6 +29,7 @@ import static org.easymock.EasyMock.newCapture;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.reset;
 import static org.easymock.EasyMock.verify;
+import static org.powermock.api.easymock.PowerMock.mockStatic;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
@@ -57,9 +58,13 @@ import org.apache.ambari.server.controller.spi.ClusterController;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.events.RequestFinishedEvent;
+import org.apache.ambari.server.orm.dao.SettingDAO;
+import org.apache.ambari.server.orm.entities.SettingEntity;
+import org.apache.ambari.server.security.authorization.AuthorizationHelper;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
 import org.apache.ambari.server.stack.NoSuchStackException;
 import org.apache.ambari.server.state.SecurityType;
+import org.apache.ambari.server.state.quicklinksprofile.QuickLinksProfile;
 import org.easymock.Capture;
 import org.easymock.EasyMock;
 import org.easymock.EasyMockRule;
@@ -68,15 +73,20 @@ import org.easymock.Mock;
 import org.easymock.MockType;
 import org.easymock.TestSubject;
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
-
-import junit.framework.Assert;
+import org.junit.runner.RunWith;
+import org.powermock.api.easymock.PowerMock;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
 
 /**
  * TopologyManager unit tests
  */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest( { TopologyManager.class })
 public class TopologyManagerTest {
 
   private static final String CLUSTER_NAME = "test-cluster";
@@ -84,6 +94,10 @@ public class TopologyManagerTest {
   private static final String BLUEPRINT_NAME = "test-bp";
   private static final String STACK_NAME = "test-stack";
   private static final String STACK_VERSION = "test-stack-version";
+  private static final String SAMPLE_QUICKLINKS_PROFILE_1 = "{\"filters\":[{\"visible\":true}],\"services\":[]}";
+  private static final String SAMPLE_QUICKLINKS_PROFILE_2 =
+      "{\"filters\":[],\"services\":[{\"name\":\"HDFS\",\"components\":[],\"filters\":[{\"visible\":true}]}]}";
+
 
   @Rule
   public EasyMockRule mocks = new EasyMockRule(this);
@@ -133,6 +147,8 @@ public class TopologyManagerTest {
   private ClusterController clusterController;
   @Mock(type = MockType.STRICT)
   private ResourceProvider resourceProvider;
+  @Mock(type = MockType.STRICT)
+  private SettingDAO settingDAO;
 
   @Mock(type = MockType.STRICT)
   private Future mockFuture;
@@ -345,13 +361,15 @@ public class TopologyManagerTest {
 
   @After
   public void tearDown() {
+    PowerMock.verify(System.class);
     verify(blueprint, stack, request, group1, group2, ambariContext, logicalRequestFactory,
         logicalRequest, configurationRequest, configurationRequest2, configurationRequest3,
-        requestStatusResponse, executor, persistedState, mockFuture);
+        requestStatusResponse, executor, persistedState, mockFuture, settingDAO);
 
+    PowerMock.reset(System.class);
     reset(blueprint, stack, request, group1, group2, ambariContext, logicalRequestFactory,
         logicalRequest, configurationRequest, configurationRequest2, configurationRequest3,
-        requestStatusResponse, executor, persistedState, mockFuture);
+        requestStatusResponse, executor, persistedState, mockFuture, settingDAO);
   }
 
   @Test
@@ -481,7 +499,7 @@ public class TopologyManagerTest {
     replay(blueprint, stack, request, group1, group2, ambariContext, logicalRequestFactory,
             configurationRequest, configurationRequest2, configurationRequest3, executor,
             persistedState, securityConfigurationFactory, credentialStoreService, clusterController, resourceProvider,
-            mockFuture, requestStatusResponse, logicalRequest);
+            mockFuture, requestStatusResponse, logicalRequest, settingDAO);
   }
 
   @Test(expected = InvalidTopologyException.class)
@@ -504,4 +522,64 @@ public class TopologyManagerTest {
     topologyManager.scaleHosts(new ScaleClusterRequest(propertySet));
     Assert.fail("InvalidTopologyException should have been thrown");
   }
+
+  @Test
+  public void testProvisionCluster_QuickLinkProfileIsSavedTheFirstTime() throws Exception {
+    expect(persistedState.getAllRequests()).andReturn(Collections.<ClusterTopology,
+        List<LogicalRequest>>emptyMap()).anyTimes();
+
+    // request has a quicklinks profile
+    expect(request.getQuickLinksProfileJson()).andReturn(SAMPLE_QUICKLINKS_PROFILE_1).anyTimes();
+
+    // this means no quicklinks profile exists before calling provisionCluster()
+    expect(settingDAO.findByName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE)).andReturn(null);
+
+    // expect that settingsDao saves the quick links profile with the right content
+    final long timeStamp = System.currentTimeMillis();
+    mockStatic(System.class);
+    expect(System.currentTimeMillis()).andReturn(timeStamp);
+    PowerMock.replay(System.class);
+    final SettingEntity quickLinksProfile = createQuickLinksSettingEntity(SAMPLE_QUICKLINKS_PROFILE_1, timeStamp);
+    settingDAO.create(eq(quickLinksProfile));
+
+    replayAll();
+
+    topologyManager.provisionCluster(request);
+  }
+
+  @Test
+  public void testProvisionCluster_ExistingQuickLinkProfileIsOverwritten() throws Exception {
+    expect(persistedState.getAllRequests()).andReturn(Collections.<ClusterTopology,
+        List<LogicalRequest>>emptyMap()).anyTimes();
+
+    // request has a quicklinks profile
+    expect(request.getQuickLinksProfileJson()).andReturn(SAMPLE_QUICKLINKS_PROFILE_2).anyTimes();
+
+    // existing quick links profile returned by dao
+    final long timeStamp1 = System.currentTimeMillis();
+    SettingEntity originalProfile = createQuickLinksSettingEntity(SAMPLE_QUICKLINKS_PROFILE_1, timeStamp1);
+    expect(settingDAO.findByName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE)).andReturn(originalProfile);
+
+    // expect that settingsDao overwrites the quick links profile with the new content
+    mockStatic(System.class);
+    final long timeStamp2 = timeStamp1 + 100;
+    expect(System.currentTimeMillis()).andReturn(timeStamp2);
+    PowerMock.replay(System.class);
+    final SettingEntity newProfile = createQuickLinksSettingEntity(SAMPLE_QUICKLINKS_PROFILE_2, timeStamp2);
+    expect(settingDAO.merge(newProfile)).andReturn(newProfile);
+
+    replayAll();
+
+    topologyManager.provisionCluster(request);
+  }
+
+  private SettingEntity createQuickLinksSettingEntity(String content, long timeStamp) {
+    SettingEntity settingEntity = new SettingEntity();
+    settingEntity.setName(QuickLinksProfile.SETTING_NAME_QUICKLINKS_PROFILE);
+    settingEntity.setSettingType(QuickLinksProfile.SETTING_TYPE_AMBARI_SERVER);
+    settingEntity.setContent(content);
+    settingEntity.setUpdatedBy(AuthorizationHelper.getAuthenticatedName());
+    settingEntity.setUpdateTimestamp(timeStamp);
+    return settingEntity;
+  }
 }


Mime
View raw message