This is an automated email from the ASF dual-hosted git repository.

benyoka pushed a commit to branch branch-feature-AMBARI-14714
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/branch-feature-AMBARI-14714 by 
this push:
     new f4cdbe8  [AMBARI-23130] Persist cluster creation request (#559)
f4cdbe8 is described below

commit f4cdbe83ac21d6433db94d292ba4c4035087ec7d
Author: benyoka <beny...@users.noreply.github.com>
AuthorDate: Wed Mar 7 10:19:58 2018 +0100

    [AMBARI-23130] Persist cluster creation request (#559)
    
    * AMBARI-23130 persist raw cluster provision request and extract stack ids 
on server restart (benyoka)
    
    * AMBARI-23130 add columnt to other DDLs + fix DDLs (benyoka)
    
    * AMBARI-23130 fix review findings (benyoka)
---
 .../controller/internal/BaseClusterRequest.java    |  21 ++++
 .../internal/ClusterResourceProvider.java          |   3 +-
 .../controller/internal/HostResourceProvider.java  |   4 +-
 .../internal/ProvisionClusterRequest.java          |   4 +-
 .../controller/internal/ScaleClusterRequest.java   |   3 +-
 .../server/orm/entities/TopologyRequestEntity.java |  18 +++
 .../BlueprintBasedClusterProvisionRequest.java     |  16 ++-
 .../server/topology/ClusterTopologyImpl.java       |  10 +-
 .../ambari/server/topology/PersistedStateImpl.java |  19 +++-
 .../ambari/server/topology/TopologyRequest.java    |  12 ++
 .../server/topology/TopologyRequestFactory.java    |   2 +-
 .../topology/TopologyRequestFactoryImpl.java       |   5 +-
 .../server/topology/TopologyRequestUtil.java       |  76 +++++++++++++
 .../src/main/resources/Ambari-DDL-Derby-CREATE.sql |   1 +
 .../src/main/resources/Ambari-DDL-MySQL-CREATE.sql |   1 +
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql    |   7 +-
 .../main/resources/Ambari-DDL-Postgres-CREATE.sql  |   1 +
 .../resources/Ambari-DDL-SQLAnywhere-CREATE.sql    |   1 +
 .../main/resources/Ambari-DDL-SQLServer-CREATE.sql |   1 +
 .../internal/ClusterResourceProviderTest.java      |  11 +-
 .../internal/ProvisionClusterRequestTest.java      |  34 +++---
 .../internal/ScaleClusterRequestTest.java          |  20 ++--
 .../server/topology/PersistedStateImplTest.java    | 123 +++++++++++++++++++++
 .../server/topology/TopologyManagerTest.java       |   2 +-
 .../server/topology/TopologyRequestUtilTest.java   |  60 ++++++++++
 25 files changed, 394 insertions(+), 61 deletions(-)

diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java
index 77eafeb..0d243a2 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java
@@ -29,6 +29,7 @@ import org.apache.ambari.server.api.predicate.Token;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
+import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.topology.Blueprint;
 import org.apache.ambari.server.topology.BlueprintFactory;
 import org.apache.ambari.server.topology.Configuration;
@@ -36,6 +37,7 @@ import org.apache.ambari.server.topology.HostGroupInfo;
 import org.apache.ambari.server.topology.InvalidTopologyTemplateException;
 import org.apache.ambari.server.topology.SecurityConfiguration;
 import org.apache.ambari.server.topology.TopologyRequest;
+import org.apache.ambari.server.topology.TopologyRequestUtil;
 
 /**
  * Provides common cluster request functionality.
@@ -54,6 +56,11 @@ public abstract class BaseClusterRequest implements 
TopologyRequest {
   protected ProvisionAction provisionAction;
 
   /**
+   * The raw request body. We would like to persist it.
+   */
+  protected String rawRequestBody;
+
+  /**
    * cluster id
    */
   protected Long clusterId;
@@ -119,6 +126,19 @@ public abstract class BaseClusterRequest implements 
TopologyRequest {
   }
 
   /**
+   * @return the raw request body in JSON string
+   */
+  public String getRawRequestBody() {
+    return rawRequestBody;
+  }
+
+  @Override
+  public Set<StackId> getStackIds() {
+    return TopologyRequestUtil.getStackIdsFromRequest(
+      TopologyRequestUtil.getPropertyMap(rawRequestBody));
+  }
+
+  /**
    * Validate that all properties specified in the predicate are valid for the 
Host resource.
    *
    * @param predicate  predicate to validate
@@ -180,6 +200,7 @@ public abstract class BaseClusterRequest implements 
TopologyRequest {
     return securityConfiguration;
   }
 
+
   /**
    * Get the host resource provider instance.
    *
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 f03231b..baf64e0 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
@@ -533,7 +533,8 @@ public class ClusterResourceProvider extends 
AbstractControllerResourceProvider
 
     ProvisionClusterRequest createClusterRequest;
     try {
-      createClusterRequest = 
topologyRequestFactory.createProvisionClusterRequest(properties, 
securityConfiguration);
+      createClusterRequest =
+        topologyRequestFactory.createProvisionClusterRequest(rawRequestBody, 
properties, securityConfiguration);
     } catch (InvalidTopologyTemplateException e) {
       throw new IllegalArgumentException("Invalid Cluster Creation Template: " 
+ e, e);
     }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
index 20146e8..18122e9 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java
@@ -1087,7 +1087,9 @@ public class HostResourceProvider extends 
AbstractControllerResourceProvider {
   private RequestStatusResponse submitHostRequests(Request request) throws 
SystemException {
     ScaleClusterRequest requestRequest;
     try {
-      requestRequest = new ScaleClusterRequest(request.getProperties());
+      requestRequest = new ScaleClusterRequest(
+        
request.getRequestInfoProperties().get(Request.REQUEST_INFO_BODY_PROPERTY),
+        request.getProperties());
     } catch (InvalidTopologyTemplateException e) {
       throw new IllegalArgumentException("Invalid Add Hosts Template: " + e, 
e);
     }
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 70b8ab5..7a5086d 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
@@ -174,8 +174,10 @@ public class ProvisionClusterRequest extends 
BaseClusterRequest implements Provi
    * @param properties  request properties
    * @param securityConfiguration  security config related properties
    */
-  public ProvisionClusterRequest(Map<String, Object> properties, 
SecurityConfiguration securityConfiguration) throws
+  public ProvisionClusterRequest(String rawRequestBody, Map<String, Object> 
properties, SecurityConfiguration securityConfiguration) throws
     InvalidTopologyTemplateException {
+    this.rawRequestBody = rawRequestBody;
+
     setClusterName(String.valueOf(properties.get(
       ClusterResourceProvider.CLUSTER_NAME_PROPERTY_ID)));
 
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ScaleClusterRequest.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ScaleClusterRequest.java
index 5e5eec8..958a3d5 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ScaleClusterRequest.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ScaleClusterRequest.java
@@ -51,7 +51,8 @@ public class ScaleClusterRequest extends BaseClusterRequest {
    *
    * @throws InvalidTopologyTemplateException if any validation of properties 
fails
    */
-  public ScaleClusterRequest(Set<Map<String, Object>> propertySet) throws 
InvalidTopologyTemplateException {
+  public ScaleClusterRequest(String rawRequestBody, Set<Map<String, Object>> 
propertySet) throws InvalidTopologyTemplateException {
+    this.rawRequestBody = rawRequestBody;
     for (Map<String, Object> properties : propertySet) {
       // can only operate on a single cluster per logical request
       if (getClusterName() == null) {
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/TopologyRequestEntity.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/TopologyRequestEntity.java
index d281838..d9ab181 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/TopologyRequestEntity.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/TopologyRequestEntity.java
@@ -75,6 +75,10 @@ public class TopologyRequestEntity {
   @Column(name = "description", length = 1024, nullable = false)
   private String description;
 
+  @Lob
+  @Column(name = "raw_request_body", length = 100000, nullable = false)
+  private String rawRequestBody;
+
   @OneToMany(mappedBy = "topologyRequestEntity", cascade = CascadeType.ALL)
   private Collection<TopologyHostGroupEntity> topologyHostGroupEntities;
 
@@ -141,6 +145,20 @@ public class TopologyRequestEntity {
     this.description = description;
   }
 
+  /**
+   * @return the raw request body in JSON
+   */
+  public String getRawRequestBody() {
+    return rawRequestBody;
+  }
+
+  /**
+   * @param rawRequestBody the raw request body in JSON
+   */
+  public void setRawRequestBody(String rawRequestBody) {
+    this.rawRequestBody = rawRequestBody;
+  }
+
   public Collection<TopologyHostGroupEntity> getTopologyHostGroupEntities() {
     return topologyHostGroupEntities;
   }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintBasedClusterProvisionRequest.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintBasedClusterProvisionRequest.java
index 43f3724..22d4bab 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintBasedClusterProvisionRequest.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintBasedClusterProvisionRequest.java
@@ -24,7 +24,6 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
-import java.util.stream.Stream;
 
 import javax.annotation.Nonnull;
 
@@ -37,7 +36,6 @@ import org.apache.ambari.server.state.StackId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
@@ -52,7 +50,7 @@ public class BlueprintBasedClusterProvisionRequest implements 
Blueprint, Provisi
   private final ProvisionClusterRequest request;
   private final Set<StackId> stackIds;
   private final StackDefinition stack;
-  private final Map<String, MpackInstance> mpacks;
+  private final Set<MpackInstance> mpacks;
   private final SecurityConfiguration securityConfiguration;
 
   public BlueprintBasedClusterProvisionRequest(AmbariContext ambariContext, 
SecurityConfigurationFactory securityConfigurationFactory, Blueprint blueprint, 
ProvisionClusterRequest request) {
@@ -61,9 +59,9 @@ public class BlueprintBasedClusterProvisionRequest implements 
Blueprint, Provisi
 
     stackIds = ImmutableSet.copyOf(Sets.union(blueprint.getStackIds(), 
request.getStackIds()));
     stack = ambariContext.composeStacks(stackIds);
-    mpacks = ImmutableMap.copyOf(
-      Stream.concat(blueprint.getMpacks().stream(), 
request.getMpacks().stream())
-        .collect(toMap(MpackInstance::getMpackName, Function.identity())));
+    mpacks = ImmutableSet.<MpackInstance>builder().
+      addAll(blueprint.getMpacks()).
+      addAll(request.getMpacks()).build();
 
     securityConfiguration = 
processSecurityConfiguration(securityConfigurationFactory);
 
@@ -104,7 +102,7 @@ public class BlueprintBasedClusterProvisionRequest 
implements Blueprint, Provisi
 
   @Override
   public Collection<MpackInstance> getMpacks() {
-    return mpacks.values();
+    return mpacks;
   }
 
   @Override
@@ -166,7 +164,7 @@ public class BlueprintBasedClusterProvisionRequest 
implements Blueprint, Provisi
 
   public Map<String, Map<String, ServiceInstance>> getServicesByMpack() {
     Map<String, Map<String, ServiceInstance>> result = new HashMap<>();
-    for (MpackInstance mpack : mpacks.values()) {
+    for (MpackInstance mpack : mpacks) {
       Map<String, ServiceInstance> services = 
mpack.getServiceInstances().stream()
         .collect(toMap(ServiceInstance::getName, Function.identity()));
       result.put(mpack.getMpackName(), services);
@@ -179,7 +177,7 @@ public class BlueprintBasedClusterProvisionRequest 
implements Blueprint, Provisi
    * whose name is unique across all mpacks.
    */
   public Map<String, ServiceInstance> getUniqueServices() {
-    Map<String, ServiceInstance> map = mpacks.values().stream()
+    Map<String, ServiceInstance> map = mpacks.stream()
       .flatMap(mpack -> mpack.getServiceInstances().stream())
       .collect(toMap(ServiceInstance::getName, Function.identity(), (s1, s2) 
-> null));
     map.entrySet().removeIf(e -> e.getValue() == null); // remove non-unique 
names mapped to null
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/ClusterTopologyImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/ClusterTopologyImpl.java
index 9581457..1ea1101 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/ClusterTopologyImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/ClusterTopologyImpl.java
@@ -46,6 +46,7 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
 
 /**
  * Represents a cluster topology.
@@ -68,18 +69,21 @@ public class ClusterTopologyImpl implements ClusterTopology 
{
   private final BlueprintBasedClusterProvisionRequest provisionRequest;
   private final String defaultPassword;
   private final Map<String, Set<ResolvedComponent>> resolvedComponents;
+  private final Setting setting;
 
   public ClusterTopologyImpl(AmbariContext ambariContext, TopologyRequest 
topologyRequest) throws InvalidTopologyException {
     this.ambariContext = ambariContext;
     this.clusterId = topologyRequest.getClusterId();
     this.blueprint = topologyRequest.getBlueprint();
+    this.setting = blueprint.getSetting();
     this.configuration = topologyRequest.getConfiguration();
     configRecommendationStrategy = ConfigRecommendationStrategy.NEVER_APPLY;
     provisionAction = topologyRequest instanceof BaseClusterRequest ? 
((BaseClusterRequest) topologyRequest).getProvisionAction() : 
INSTALL_AND_START; // FIXME
 
     provisionRequest = null;
     defaultPassword = null;
-    stackIds = topologyRequest.getBlueprint().getStackIds();
+    stackIds = ImmutableSet.copyOf(
+      Sets.union(topologyRequest.getStackIds(), 
topologyRequest.getBlueprint().getStackIds()));
     stack = ambariContext.composeStacks(stackIds);
     resolvedComponents = ImmutableMap.of();
 
@@ -104,7 +108,7 @@ public class ClusterTopologyImpl implements ClusterTopology 
{
     defaultPassword = provisionRequest.getDefaultPassword();
     stackIds = request.getStackIds();
     stack = request.getStack();
-
+    setting = request.getSetting();
     
blueprint.getConfiguration().setParentConfiguration(stack.getConfiguration(getServices()));
     registerHostGroupInfo(request.getHostGroupInfo());
   }
@@ -150,7 +154,7 @@ public class ClusterTopologyImpl implements ClusterTopology 
{
 
   @Override
   public Setting getSetting() {
-    return provisionRequest.getSetting();
+    return setting;
   }
 
   @Override
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/PersistedStateImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/PersistedStateImpl.java
index 13c9eff..ae70db4 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/PersistedStateImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/PersistedStateImpl.java
@@ -23,7 +23,9 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
+import javax.inject.Inject;
 import javax.inject.Singleton;
 
 import org.apache.ambari.server.AmbariException;
@@ -50,12 +52,12 @@ import 
org.apache.ambari.server.orm.entities.TopologyLogicalTaskEntity;
 import org.apache.ambari.server.orm.entities.TopologyRequestEntity;
 import org.apache.ambari.server.stack.NoSuchStackException;
 import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.topology.tasks.TopologyTask;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.gson.Gson;
-import com.google.inject.Inject;
 import com.google.inject.persist.Transactional;
 
 /**
@@ -92,9 +94,6 @@ public class PersistedStateImpl implements PersistedState {
   private HostRoleCommandDAO hostRoleCommandDAO;
 
   @Inject
-  private HostRoleCommandDAO physicalTaskDAO;
-
-  @Inject
   private BlueprintFactory blueprintFactory;
 
   @Inject
@@ -255,6 +254,8 @@ public class PersistedStateImpl implements PersistedState {
   private TopologyRequestEntity toEntity(BaseClusterRequest request) {
     TopologyRequestEntity entity = new TopologyRequestEntity();
 
+    entity.setRawRequestBody(request.getRawRequestBody());
+
     //todo: this isn't set for a scaling operation because we had intended to 
allow multiple
     //todo: bp's to be used to scale a cluster although this isn't currently 
supported by
     //todo: new topology infrastructure
@@ -330,7 +331,7 @@ public class PersistedStateImpl implements PersistedState {
           logicalTaskEntity.setTopologyHostTaskEntity(topologyTaskEntity);
           Long physicalId = request.getPhysicalTaskId(logicalTaskId);
           if (physicalId != null) {
-            
logicalTaskEntity.setHostRoleCommandEntity(physicalTaskDAO.findByPK(physicalId));
+            
logicalTaskEntity.setHostRoleCommandEntity(hostRoleCommandDAO.findByPK(physicalId));
           }
           logicalTaskEntity.setTopologyHostTaskEntity(topologyTaskEntity);
         }
@@ -391,6 +392,7 @@ public class PersistedStateImpl implements PersistedState {
     private final Configuration configuration;
     private final Map<String, HostGroupInfo> hostGroupInfoMap = new 
HashMap<>();
     private final ProvisionAction provisionAction;
+    private final Set<StackId> stackIds;
 
     public ReplayedTopologyRequest(TopologyRequestEntity entity, 
BlueprintFactory blueprintFactory) {
       clusterId = entity.getClusterId();
@@ -398,6 +400,8 @@ public class PersistedStateImpl implements PersistedState {
       description = entity.getDescription();
       provisionAction = entity.getProvisionAction();
 
+      stackIds = 
TopologyRequestUtil.getStackIdsFromRequest(entity.getRawRequestBody());
+
       try {
         blueprint = blueprintFactory.getBlueprint(entity.getBlueprintName());
       } catch (NoSuchStackException e) {
@@ -410,6 +414,11 @@ public class PersistedStateImpl implements PersistedState {
     }
 
     @Override
+    public Set<StackId> getStackIds() {
+      return stackIds;
+    }
+
+    @Override
     public Long getClusterId() {
       return clusterId;
     }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequest.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequest.java
index 94fcf23..ffdf1c7 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequest.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequest.java
@@ -18,7 +18,12 @@
 
 package org.apache.ambari.server.topology;
 
+import static java.util.Collections.emptySet;
+
 import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.state.StackId;
 
 /**
  * A request which is used to create or modify a cluster topology.
@@ -70,4 +75,11 @@ public interface TopologyRequest {
    * @return string description of the request
    */
   String getDescription();
+
+  /**
+   * @return a set of stack id's if supported by the TopologyRequest.
+   */
+  default Set<StackId> getStackIds() {
+    return emptySet();
+  }
 }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequestFactory.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequestFactory.java
index 751e2d7..136be39 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequestFactory.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequestFactory.java
@@ -28,6 +28,6 @@ import 
org.apache.ambari.server.controller.internal.ProvisionClusterRequest;
  */
 public interface TopologyRequestFactory {
 
-  ProvisionClusterRequest createProvisionClusterRequest(Map<String, Object> 
properties, SecurityConfiguration securityConfiguration) throws 
InvalidTopologyTemplateException;
+  ProvisionClusterRequest createProvisionClusterRequest(String rawRequestBody, 
Map<String, Object> properties, SecurityConfiguration securityConfiguration) 
throws InvalidTopologyTemplateException;
   // todo: use to create other request types
 }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequestFactoryImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequestFactoryImpl.java
index 44f0d1f..50d3fa1 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequestFactoryImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequestFactoryImpl.java
@@ -29,8 +29,7 @@ import 
org.apache.ambari.server.controller.internal.ProvisionClusterRequest;
 public class TopologyRequestFactoryImpl implements TopologyRequestFactory {
 
   @Override
-  public ProvisionClusterRequest createProvisionClusterRequest(Map<String, 
Object> properties, SecurityConfiguration securityConfiguration) throws 
InvalidTopologyTemplateException {
-    return new ProvisionClusterRequest(properties, securityConfiguration);
-
+  public ProvisionClusterRequest createProvisionClusterRequest(String 
rawRequestBody, Map<String, Object> properties, SecurityConfiguration 
securityConfiguration) throws InvalidTopologyTemplateException {
+    return new ProvisionClusterRequest(rawRequestBody, properties, 
securityConfiguration);
   }
 }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequestUtil.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequestUtil.java
new file mode 100644
index 0000000..1b160a4
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/topology/TopologyRequestUtil.java
@@ -0,0 +1,76 @@
+/*
+ * 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.topology;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
+import static java.util.stream.Collectors.toSet;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.utils.JsonUtils;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+/**
+ * Utility functions for topology requests.
+ */
+public class TopologyRequestUtil {
+
+  public static final String NAME = "name";
+  public static final String VERSION = "version";
+
+
+  /**
+   * @param rawRequestJson The topology request in raw JSON format. Null input 
is handled gracefully.
+   * @return a Set of stack id's contained in the request
+   */
+  public static Set<StackId> getStackIdsFromRequest(String rawRequestJson) {
+    return getStackIdsFromRequest(getPropertyMap(rawRequestJson));
+  }
+
+
+  /**
+   * @param rawRequestMap The topology request in raw JSON format. Null input 
is handled gracefully.
+   * @return a Set of stack id's contained in the request
+   */
+  public static Set<StackId> getStackIdsFromRequest(Map<String, Object> 
rawRequestMap) {
+    List<Map<String, String>> mpackInstances = (List<Map<String, String>>)
+      rawRequestMap.getOrDefault("mpack_instances", emptyList());
+    return mpackInstances.stream().map(m -> {
+      checkArgument(m.containsKey(NAME), "Missing mpack name");
+      checkArgument(m.containsKey(VERSION), "Missing mpack version");
+      return new StackId(m.get(NAME), m.get(VERSION));
+    }).collect(toSet());
+  }
+
+  /**
+   * @param rawRequestJson The topology request in raw JSON format. Null input 
is handled gracefully.
+   * @return the request body parsed as map (null is parsed as empty map)
+   */
+  public static Map<String, Object> getPropertyMap(String rawRequestJson) {
+    return null == rawRequestJson ?
+      emptyMap() :
+      JsonUtils.fromJson(rawRequestJson, new TypeReference<Map<String, 
Object>>() {});
+  }
+}
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql 
b/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
index e714665..e523466 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Derby-CREATE.sql
@@ -893,6 +893,7 @@ CREATE TABLE topology_request (
   action VARCHAR(255) NOT NULL,
   cluster_id BIGINT NOT NULL,
   bp_name VARCHAR(100) NOT NULL,
+  raw_request_body CLOB NOT NULL,
   cluster_properties VARCHAR(3000),
   cluster_attributes VARCHAR(3000),
   description VARCHAR(1024),
diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql 
b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
index 8e87d56..827d62e 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -910,6 +910,7 @@ CREATE TABLE topology_request (
   action VARCHAR(255) NOT NULL,
   cluster_id BIGINT NOT NULL,
   bp_name VARCHAR(100) NOT NULL,
+  raw_request_body LONGTEXT NOT NULL,
   cluster_properties LONGTEXT,
   cluster_attributes LONGTEXT,
   description VARCHAR(1024),
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql 
b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
index 9bb13aa..116122a 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -136,7 +136,8 @@ CREATE TABLE servicegroups (
   stack_id NUMBER(19) NOT NULL,
   CONSTRAINT PK_servicegroups PRIMARY KEY (id, cluster_id),
   CONSTRAINT FK_servicegroups_cluster_id FOREIGN KEY (cluster_id) REFERENCES 
clusters (cluster_id),
-  CONSTRAINT FK_servicegroups_stack_id FOREIGN KEY (stack_id) REFERENCES stack 
(stack_id));
+  CONSTRAINT FK_servicegroups_stack_id FOREIGN KEY (stack_id) REFERENCES stack 
(stack_id),
+  CONSTRAINT UQ_TEMP_UNTIL_REAL_PK UNIQUE(id));
 
 CREATE TABLE servicegroupdependencies (
   id NUMBER(19) NOT NULL,
@@ -208,8 +209,7 @@ CREATE TABLE serviceconfig (
   CONSTRAINT PK_serviceconfig PRIMARY KEY (service_config_id),
   CONSTRAINT FK_serviceconfig_stack_id FOREIGN KEY (stack_id) REFERENCES 
stack(stack_id),
   CONSTRAINT FK_serviceconfig_clstr_svc FOREIGN KEY (service_id, 
service_group_id, cluster_id) REFERENCES clusterservices (id, service_group_id, 
cluster_id),
-  CONSTRAINT UQ_scv_service_version UNIQUE (cluster_id, service_id, version),
-  CONSTRAINT UQ_TEMP_UNTIL_REAL_PK UNIQUE(id));
+  CONSTRAINT UQ_scv_service_version UNIQUE (cluster_id, service_id, version)  
);
 
 CREATE TABLE serviceconfighosts (
   service_config_id NUMBER(19) NOT NULL,
@@ -888,6 +888,7 @@ CREATE TABLE topology_request (
   action VARCHAR(255) NOT NULL,
   cluster_id NUMBER(19) NOT NULL,
   bp_name VARCHAR(100) NOT NULL,
+  raw_request_body CLOB NOT NULL,
   cluster_properties CLOB,
   cluster_attributes CLOB,
   description VARCHAR(1024),
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql 
b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
index b0907a3..99dcbe8 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -894,6 +894,7 @@ CREATE TABLE topology_request (
   action VARCHAR(255) NOT NULL,
   cluster_id BIGINT NOT NULL,
   bp_name VARCHAR(100) NOT NULL,
+  raw_request_body TEXT NOT NULL,
   cluster_properties TEXT,
   cluster_attributes TEXT,
   description VARCHAR(1024),
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql 
b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
index ad0b71f..bc635e0 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
@@ -888,6 +888,7 @@ CREATE TABLE topology_request (
   action VARCHAR(255) NOT NULL,
   cluster_id NUMERIC(19) NOT NULL,
   bp_name VARCHAR(100) NOT NULL,
+  raw_request_body TEXT NOT NULL,
   cluster_properties TEXT,
   cluster_attributes TEXT,
   description VARCHAR(1024),
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql 
b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
index 041011c..1de62da 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
@@ -911,6 +911,7 @@ CREATE TABLE topology_request (
   action VARCHAR(255) NOT NULL,
   cluster_id BIGINT NOT NULL,
   bp_name VARCHAR(100) NOT NULL,
+  raw_request_body TEXT NOT NULL,
   cluster_properties TEXT,
   cluster_attributes TEXT,
   description VARCHAR(1024),
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java
index 5e1ba49..06d95bd 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java
@@ -20,6 +20,7 @@ package org.apache.ambari.server.controller.internal;
 
 import static org.easymock.EasyMock.anyBoolean;
 import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.anyString;
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.createNiceMock;
@@ -154,7 +155,7 @@ public class ClusterResourceProviderTest {
 
     
expect(securityFactory.createSecurityConfigurationFromRequest(EasyMock.anyObject(),
 anyBoolean())).andReturn(null)
       .once();
-    expect(topologyFactory.createProvisionClusterRequest(properties, 
null)).andReturn(topologyRequest).once();
+    expect(topologyFactory.createProvisionClusterRequest("{}", properties, 
null)).andReturn(topologyRequest).once();
     
expect(topologyManager.provisionCluster(topologyRequest)).andReturn(requestStatusResponse).once();
     expect(requestStatusResponse.getRequestId()).andReturn(5150L).anyTimes();
 
@@ -184,7 +185,7 @@ public class ClusterResourceProviderTest {
     expect(request.getProperties()).andReturn(requestProperties).anyTimes();
     
expect(request.getRequestInfoProperties()).andReturn(requestInfoProperties).anyTimes();
 
-    expect(topologyFactory.createProvisionClusterRequest(properties, 
securityConfiguration)).andReturn(topologyRequest).once();
+    expect(topologyFactory.createProvisionClusterRequest(anyString(), 
eq(properties), eq(securityConfiguration))).andReturn(topologyRequest).once();
     
expect(securityFactory.createSecurityConfigurationFromRequest(EasyMock.anyObject(),
 anyBoolean())).andReturn
       (securityConfiguration).once();
     
expect(topologyManager.provisionCluster(topologyRequest)).andReturn(requestStatusResponse).once();
@@ -208,7 +209,7 @@ public class ClusterResourceProviderTest {
     // set expectations
     expect(request.getProperties()).andReturn(requestProperties).anyTimes();
     // throw exception from topology request factory an assert that the 
correct exception is thrown from resource provider
-    expect(topologyFactory.createProvisionClusterRequest(properties, 
null)).andThrow(new InvalidTopologyException
+    expect(topologyFactory.createProvisionClusterRequest(null, properties, 
null)).andThrow(new InvalidTopologyException
       ("test"));
 
     replayAll();
@@ -472,7 +473,7 @@ public class ClusterResourceProviderTest {
 
     
expect(securityFactory.createSecurityConfigurationFromRequest(EasyMock.anyObject(),
 anyBoolean())).andReturn(null)
         .once();
-    expect(topologyFactory.createProvisionClusterRequest(properties, 
null)).andReturn(topologyRequest).once();
+    expect(topologyFactory.createProvisionClusterRequest(anyString(), 
eq(properties), anyObject())).andReturn(topologyRequest).once();
     
expect(topologyManager.provisionCluster(topologyRequest)).andReturn(requestStatusResponse).once();
     expect(requestStatusResponse.getRequestId()).andReturn(5150L).anyTimes();
 
@@ -803,7 +804,7 @@ public class ClusterResourceProviderTest {
 
     
expect(securityFactory.createSecurityConfigurationFromRequest(EasyMock.anyObject(),
 anyBoolean())).andReturn(null)
         .once();
-    expect(topologyFactory.createProvisionClusterRequest(properties, 
null)).andReturn(topologyRequest).once();
+    expect(topologyFactory.createProvisionClusterRequest("{}", properties, 
null)).andReturn(topologyRequest).once();
     
expect(topologyManager.provisionCluster(topologyRequest)).andReturn(requestStatusResponse).once();
     expect(requestStatusResponse.getRequestId()).andReturn(5150L).anyTimes();
 
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 5ed582f..3cc4890 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
@@ -109,7 +109,7 @@ public class ProvisionClusterRequestTest {
     replay(hostResourceProvider);
     Map<String, Object> properties = 
createBlueprintRequestPropertiesNameOnly(CLUSTER_NAME, BLUEPRINT_NAME);
 
-    ProvisionClusterRequest provisionClusterRequest = new 
ProvisionClusterRequest(properties, null);
+    ProvisionClusterRequest provisionClusterRequest = new 
ProvisionClusterRequest(null, properties, null);
 
     assertEquals(CLUSTER_NAME, provisionClusterRequest.getClusterName());
     assertEquals(TopologyRequest.Type.PROVISION, 
provisionClusterRequest.getType());
@@ -160,7 +160,7 @@ public class ProvisionClusterRequestTest {
     replay(hostResourceProvider);
     Map<String, Object> properties = 
createBlueprintRequestPropertiesCountOnly(CLUSTER_NAME, BLUEPRINT_NAME);
 
-    ProvisionClusterRequest provisionClusterRequest = new 
ProvisionClusterRequest(properties, null);
+    ProvisionClusterRequest provisionClusterRequest = new 
ProvisionClusterRequest(null, properties, null);
 
     assertEquals(CLUSTER_NAME, provisionClusterRequest.getClusterName());
     assertEquals(TopologyRequest.Type.PROVISION, 
provisionClusterRequest.getType());
@@ -211,7 +211,7 @@ public class ProvisionClusterRequestTest {
   @Test
   public void testMultipleGroups() throws Exception {
     Map<String, Object> properties = 
createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
-    ProvisionClusterRequest provisionClusterRequest = new 
ProvisionClusterRequest(properties, null);
+    ProvisionClusterRequest provisionClusterRequest = new 
ProvisionClusterRequest(null, properties, null);
 
     assertEquals(CLUSTER_NAME, provisionClusterRequest.getClusterName());
     assertEquals(TopologyRequest.Type.PROVISION, 
provisionClusterRequest.getType());
@@ -286,7 +286,7 @@ public class ProvisionClusterRequestTest {
     reset(hostResourceProvider);
     replay(hostResourceProvider);
     // should result in an exception
-    new ProvisionClusterRequest(properties, null);
+    new ProvisionClusterRequest(null, properties, null);
   }
 
   @Test
@@ -301,7 +301,7 @@ public class ProvisionClusterRequestTest {
     credentialsSet.add(credentialHashMap);
     properties.put("credentials", credentialsSet);
 
-    ProvisionClusterRequest provisionClusterRequest = new 
ProvisionClusterRequest(properties, null);
+    ProvisionClusterRequest provisionClusterRequest = new 
ProvisionClusterRequest(null, properties, null);
 
     
assertEquals(provisionClusterRequest.getCredentialsMap().get("testAlias").getAlias(),
 "testAlias");
     
assertEquals(provisionClusterRequest.getCredentialsMap().get("testAlias").getPrincipal(),
 "testPrincipal");
@@ -326,7 +326,7 @@ public class ProvisionClusterRequestTest {
     credentialsSet.add(credentialHashMap);
     properties.put("credentials", credentialsSet);
 
-    ProvisionClusterRequest provisionClusterRequest = new 
ProvisionClusterRequest(properties, null);
+    ProvisionClusterRequest provisionClusterRequest = new 
ProvisionClusterRequest(null, properties, null);
   }
 
   @Test(expected= InvalidTopologyTemplateException.class)
@@ -338,7 +338,7 @@ public class ProvisionClusterRequestTest {
     reset(hostResourceProvider);
     replay(hostResourceProvider);
     // should result in an exception
-    new ProvisionClusterRequest(properties, null);
+    new ProvisionClusterRequest(null, properties, null);
   }
 
   @Test(expected= InvalidTopologyTemplateException.class)
@@ -350,7 +350,7 @@ public class ProvisionClusterRequestTest {
     reset(hostResourceProvider);
     replay(hostResourceProvider);
     // should result in an exception
-    new ProvisionClusterRequest(properties, null);
+    new ProvisionClusterRequest(null, properties, null);
   }
 
   @Test(expected = InvalidTopologyTemplateException.class)
@@ -370,7 +370,7 @@ public class ProvisionClusterRequestTest {
     reset(hostResourceProvider);
     replay(hostResourceProvider);
     // should result in an exception
-    new ProvisionClusterRequest(properties, null);
+    new ProvisionClusterRequest(null, properties, null);
   }
 
 
@@ -383,7 +383,7 @@ public class ProvisionClusterRequestTest {
     replay(hostResourceProvider);
 
     // should result in an exception due to invalid property in host predicate
-    new ProvisionClusterRequest(createBlueprintRequestProperties(CLUSTER_NAME, 
BLUEPRINT_NAME), null);
+    new ProvisionClusterRequest(null, 
createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME), null);
   }
 
   @Test(expected = InvalidTopologyTemplateException.class)
@@ -395,7 +395,7 @@ public class ProvisionClusterRequestTest {
     Map<String, Object> properties = 
createBlueprintRequestPropertiesNameOnly(CLUSTER_NAME, BLUEPRINT_NAME);
     ((Map) ((List) 
properties.get("host_groups")).iterator().next()).put("host_count", "5");
     // should result in an exception due to both host name and host count 
being specified
-    new ProvisionClusterRequest(properties, null);
+    new ProvisionClusterRequest(null, properties, null);
   }
 
   @Test(expected = InvalidTopologyTemplateException.class)
@@ -407,13 +407,13 @@ public class ProvisionClusterRequestTest {
     Map<String, Object> properties = 
createBlueprintRequestPropertiesNameOnly(CLUSTER_NAME, BLUEPRINT_NAME);
     ((Map) ((List) 
properties.get("host_groups")).iterator().next()).put("host_predicate", 
"Hosts/host_name=myTestHost");
     // should result in an exception due to both host name and host count 
being specified
-    new ProvisionClusterRequest(properties, null);
+    new ProvisionClusterRequest(null, properties, null);
   }
 
   @Test
   public void testQuickLinksProfile_NoDataInRequest() throws Exception {
     Map<String, Object> properties = 
createBlueprintRequestProperties(CLUSTER_NAME, BLUEPRINT_NAME);
-    ProvisionClusterRequest request = new ProvisionClusterRequest(properties, 
null);
+    ProvisionClusterRequest request = new ProvisionClusterRequest(null, 
properties, null);
     assertNull("No quick links profile is expected", 
request.getQuickLinksProfileJson());
   }
 
@@ -424,7 +424,7 @@ public class ProvisionClusterRequestTest {
     properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_FILTERS_PROPERTY,
         Sets.newHashSet(QuickLinksProfileBuilderTest.filter(null, null, 
true)));
 
-    ProvisionClusterRequest request = new ProvisionClusterRequest(properties, 
null);
+    ProvisionClusterRequest request = new ProvisionClusterRequest(null, 
properties, null);
     assertEquals("Quick links profile doesn't match expected",
         "{\"filters\":[{\"visible\":true}],\"services\":[]}",
         request.getQuickLinksProfileJson());
@@ -439,7 +439,7 @@ public class ProvisionClusterRequestTest {
     Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
     
properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_SERVICES_PROPERTY, 
services);
 
-    ProvisionClusterRequest request = new ProvisionClusterRequest(properties, 
null);
+    ProvisionClusterRequest request = new ProvisionClusterRequest(null, 
properties, null);
     assertEquals("Quick links profile doesn't match expected",
         
"{\"filters\":[],\"services\":[{\"name\":\"HDFS\",\"components\":[],\"filters\":[{\"visible\":true}]}]}",
         request.getQuickLinksProfileJson());
@@ -457,7 +457,7 @@ public class ProvisionClusterRequestTest {
     Set<Map<String, Object>> services = Sets.newHashSet(hdfs);
     
properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_SERVICES_PROPERTY, 
services);
 
-    ProvisionClusterRequest request = new ProvisionClusterRequest(properties, 
null);
+    ProvisionClusterRequest request = new ProvisionClusterRequest(null, 
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}]}]}",
@@ -470,7 +470,7 @@ public class ProvisionClusterRequestTest {
 
     
properties.put(ProvisionClusterRequest.QUICKLINKS_PROFILE_SERVICES_PROPERTY, 
"Hello World!");
 
-    ProvisionClusterRequest request = new ProvisionClusterRequest(properties, 
null);
+    ProvisionClusterRequest request = new ProvisionClusterRequest(null, 
properties, null);
   }
 
   public static Map<String, Object> createBlueprintRequestProperties(String 
clusterName, String blueprintName) {
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ScaleClusterRequestTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ScaleClusterRequestTest.java
index b9f32a0..cc02db4 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ScaleClusterRequestTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ScaleClusterRequestTest.java
@@ -112,7 +112,7 @@ public class ScaleClusterRequestTest {
     reset(hostResourceProvider);
     replay(hostResourceProvider);
 
-    ScaleClusterRequest scaleClusterRequest = new 
ScaleClusterRequest(Collections.singleton(props));
+    ScaleClusterRequest scaleClusterRequest = new ScaleClusterRequest("{}", 
Collections.singleton(props));
 
     assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
     assertEquals(String.format("Scale Cluster '%s' (+%s hosts)", CLUSTER_NAME, 
"1"),
@@ -150,7 +150,7 @@ public class ScaleClusterRequestTest {
     reset(hostResourceProvider);
     replay(hostResourceProvider);
 
-    ScaleClusterRequest scaleClusterRequest = new 
ScaleClusterRequest(propertySet);
+    ScaleClusterRequest scaleClusterRequest = new ScaleClusterRequest("{}", 
propertySet);
 
     assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
     assertEquals(String.format("Scale Cluster '%s' (+%s hosts)", CLUSTER_NAME, 
"2"),
@@ -177,7 +177,7 @@ public class ScaleClusterRequestTest {
     reset(hostResourceProvider);
     replay(hostResourceProvider);
 
-    ScaleClusterRequest scaleClusterRequest = new 
ScaleClusterRequest(Collections.singleton(
+    ScaleClusterRequest scaleClusterRequest = new ScaleClusterRequest("{}", 
Collections.singleton(
         createScaleClusterPropertiesGroup1_HostCount(CLUSTER_NAME, 
BLUEPRINT_NAME)));
 
     assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
@@ -203,7 +203,7 @@ public class ScaleClusterRequestTest {
     reset(hostResourceProvider);
     replay(hostResourceProvider);
 
-    ScaleClusterRequest scaleClusterRequest = new 
ScaleClusterRequest(Collections.singleton(
+    ScaleClusterRequest scaleClusterRequest = new ScaleClusterRequest("{}", 
Collections.singleton(
         createScaleClusterPropertiesGroup1_HostCount2(CLUSTER_NAME, 
BLUEPRINT_NAME)));
 
     assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
@@ -225,7 +225,7 @@ public class ScaleClusterRequestTest {
 
   @Test
   public void test_basic_hostCountAndPredicate() throws Exception {
-    ScaleClusterRequest scaleClusterRequest = new 
ScaleClusterRequest(Collections.singleton(
+    ScaleClusterRequest scaleClusterRequest = new ScaleClusterRequest("{}", 
Collections.singleton(
         createScaleClusterPropertiesGroup1_HostCountAndPredicate(CLUSTER_NAME, 
BLUEPRINT_NAME)));
 
     assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
@@ -252,7 +252,7 @@ public class ScaleClusterRequestTest {
     propertySet.add(createScaleClusterPropertiesGroup1_HostCount(CLUSTER_NAME, 
BLUEPRINT_NAME));
     propertySet.add(createScaleClusterPropertiesGroup1_HostName(CLUSTER_NAME, 
BLUEPRINT_NAME));
 
-    ScaleClusterRequest scaleClusterRequest = new 
ScaleClusterRequest(propertySet);
+    ScaleClusterRequest scaleClusterRequest = new ScaleClusterRequest("{}", 
propertySet);
 
     assertEquals(TopologyRequest.Type.SCALE, scaleClusterRequest.getType());
     assertEquals(String.format("Scale Cluster '%s' (+%s hosts)", CLUSTER_NAME, 
"3"),
@@ -300,7 +300,7 @@ public class ScaleClusterRequestTest {
     reset(hostResourceProvider);
     replay(hostResourceProvider);
     // should result in an exception
-    new ScaleClusterRequest(Collections.singleton(properties));
+    new ScaleClusterRequest("{}", Collections.singleton(properties));
   }
 
   @Test(expected = InvalidTopologyTemplateException.class)
@@ -313,7 +313,7 @@ public class ScaleClusterRequestTest {
     reset(hostResourceProvider);
     replay(hostResourceProvider);
     // should result in an exception because neither host name or host count 
are specified
-    new ScaleClusterRequest(Collections.singleton(properties));
+    new ScaleClusterRequest("{}", Collections.singleton(properties));
   }
 
 
@@ -326,7 +326,7 @@ public class ScaleClusterRequestTest {
     replay(hostResourceProvider);
 
     // should result in an exception due to invalid property in host predicate
-    new ScaleClusterRequest(Collections.singleton(
+    new ScaleClusterRequest("{}", Collections.singleton(
         createScaleClusterPropertiesGroup1_HostCountAndPredicate(CLUSTER_NAME, 
BLUEPRINT_NAME)));
   }
 
@@ -340,7 +340,7 @@ public class ScaleClusterRequestTest {
     propertySet.add(createScaleClusterPropertiesGroup1_HostName2(CLUSTER_NAME, 
"OTHER_BLUEPRINT"));
 
     // should result in an exception due to different blueprints being 
specified
-    new ScaleClusterRequest(propertySet);
+    new ScaleClusterRequest("{}", propertySet);
   }
 
   public static Map<String, Object> 
createScaleClusterPropertiesGroup1_HostName(String clusterName, String 
blueprintName) {
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/topology/PersistedStateImplTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/topology/PersistedStateImplTest.java
new file mode 100644
index 0000000..7f6ee4b
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/topology/PersistedStateImplTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.topology;
+
+
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.newCapture;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.junit.Assert.assertEquals;
+
+import java.lang.reflect.Field;
+
+import org.apache.ambari.server.controller.internal.BaseClusterRequest;
+import org.apache.ambari.server.controller.internal.ProvisionAction;
+import org.apache.ambari.server.controller.internal.ProvisionClusterRequest;
+import org.apache.ambari.server.orm.dao.TopologyRequestDAO;
+import org.apache.ambari.server.orm.entities.TopologyRequestEntity;
+import org.easymock.Capture;
+import org.easymock.EasyMockRunner;
+import org.easymock.Mock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import com.google.common.collect.ImmutableMap;
+
+
+@RunWith(EasyMockRunner.class)
+public class PersistedStateImplTest {
+
+  private static final String CLUSTER_REQUEST =
+    "{'blueprint': 'bp', 'host_groups': [{'name': 'group','host_count': '1' 
}]}".replace('\'', '"');
+
+  private static final String BLUEPRINT_NAME = "bp";
+
+  @Mock
+  private TopologyRequestDAO topologyRequestDAO;
+
+  @Mock
+  private BlueprintFactory blueprintFactory;
+
+  @Mock
+  private Blueprint blueprint;
+
+  @Mock
+  private ProvisionClusterRequest
+    request;
+
+  private PersistedStateImpl persistedState;
+
+  @Before
+  public void init() throws Exception {
+    expect(blueprint.getName()).andReturn(BLUEPRINT_NAME).anyTimes();
+    expect(blueprint.getConfiguration()).andReturn(new 
Configuration()).anyTimes();
+    
expect(blueprintFactory.getBlueprint(BLUEPRINT_NAME)).andReturn(blueprint).anyTimes();
+
+    expect(request.getBlueprint()).andReturn(blueprint).anyTimes();
+    expect(request.getRawRequestBody()).andReturn(CLUSTER_REQUEST).anyTimes();
+    
expect(request.getType()).andReturn(TopologyRequest.Type.PROVISION).anyTimes();
+    expect(request.getConfiguration()).andReturn(new 
Configuration()).anyTimes();
+    expect(request.getClusterId()).andReturn(1L).anyTimes();
+    expect(request.getDescription()).andReturn("").anyTimes();
+    
expect(request.getProvisionAction()).andReturn(ProvisionAction.INSTALL_AND_START).anyTimes();
+    HostGroupInfo hostGroupInfo = new HostGroupInfo("hostgroup1");
+    hostGroupInfo.setConfiguration(new Configuration());
+    expect(request.getHostGroupInfo()).andReturn(ImmutableMap.of("hostgroup1", 
hostGroupInfo)).anyTimes();
+
+    replay(blueprint, blueprintFactory, request);
+
+    Field blueprintFactoryField = 
BaseClusterRequest.class.getDeclaredField("blueprintFactory");
+    blueprintFactoryField.setAccessible(true);
+    blueprintFactoryField.set(null, blueprintFactory);
+
+    persistedState = new PersistedStateImpl();
+    Field topologyRequestDAOField = 
PersistedStateImpl.class.getDeclaredField("topologyRequestDAO");
+    topologyRequestDAOField.setAccessible(true);
+    topologyRequestDAOField.set(persistedState, topologyRequestDAO);
+  }
+
+  @After
+  public void tearDown() {
+    reset(topologyRequestDAO, blueprintFactory, blueprint, request);
+  }
+
+  @Test
+  public void testPersistTopologyRequest_RawRequestIsSaved() throws Exception {
+    // Given
+    Capture<TopologyRequestEntity> entityCapture = newCapture();
+    topologyRequestDAO.create(capture(entityCapture));
+    expectLastCall().andAnswer(() -> {
+      entityCapture.getValue().setId(1L);
+      return null;
+    });
+    replay(topologyRequestDAO);
+
+    // When
+    persistedState.persistTopologyRequest(request);
+
+    // Then
+    assertEquals(CLUSTER_REQUEST, 
entityCapture.getValue().getRawRequestBody());
+  }
+
+}
\ No newline at end of file
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 9f3a1f0..89f2f4f 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
@@ -584,7 +584,7 @@ public class TopologyManagerTest {
     
expect(persistedState.getAllRequests()).andReturn(Collections.emptyMap()).anyTimes();
     replayAll();
     topologyManager.provisionCluster(request);
-    topologyManager.scaleHosts(new ScaleClusterRequest(propertySet));
+    topologyManager.scaleHosts(new ScaleClusterRequest("{}", propertySet));
     Assert.fail("InvalidTopologyException should have been thrown");
   }
 
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyRequestUtilTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyRequestUtilTest.java
new file mode 100644
index 0000000..db4046a
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/topology/TopologyRequestUtilTest.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.topology;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Collections;
+
+import org.apache.ambari.server.state.StackId;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+public class TopologyRequestUtilTest {
+
+  private static final String REQUEST_WITH_MPACK_INSTANCES =
+    "{ 'mpack_instances' : [ {'name': 'HDPCORE', 'version': '1.0.0-b98'}, 
{'name': 'EDW', 'version': '1.0.0'} ] }".replace('\'', '"');
+
+  private static final String REQUEST_WITH_INVALID_MPACK_INSTANCE =
+    "{ 'mpack_instances' : [ {'name': 'HDPCORE', 'version': '1.0.0-b98'}, 
{'name': 'EDW'} ] }".replace('\'', '"');
+
+  private static final String REQUEST_WITHOUT_MPACK_INSTANCE = "{}";
+
+
+  @Test
+  public void testGetStackIdsFromRawRequest_normalCase() {
+    assertEquals(
+      ImmutableSet.of(new StackId("HDPCORE", "1.0.0-b98"), new StackId("EDW", 
"1.0.0")),
+      
TopologyRequestUtil.getStackIdsFromRequest(REQUEST_WITH_MPACK_INSTANCES));
+  }
+
+  @Test
+  public void testGetStackIdsFromRawRequest_noMpackInstances() {
+    assertEquals(
+      Collections.emptySet(),
+      
TopologyRequestUtil.getStackIdsFromRequest(REQUEST_WITHOUT_MPACK_INSTANCE));
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void testGetStackIdsFromRawRequest_wrongMpackInstance() {
+    
TopologyRequestUtil.getStackIdsFromRequest(REQUEST_WITH_INVALID_MPACK_INSTANCE);
+  }
+
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
beny...@apache.org.

Reply via email to