This is an automated email from the ASF dual-hosted git repository.
mpapirkovskyy pushed a commit to branch branch-2.7
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/branch-2.7 by this push:
new ca6e695 AMBARI-25392. Backport: Performance Tune Hosts and service
Configs Pages to 2.7.5 (#3099)
ca6e695 is described below
commit ca6e6956e794df34c5f43ec5512f0286da10c57a
Author: Myroslav Papirkovskyi <[email protected]>
AuthorDate: Fri Oct 11 20:37:15 2019 +0300
AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages to
2.7.5 (#3099)
* AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages
to 2.7.5. (AMBARI-24876. BE: Performance Tune service Configs Pages.)
(mpapirkovskyy)
* AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages
to 2.7.5. (AMBARI-24930. Recommendation configs request was failed with custom
config group.) (mpapirkovskyy)
* AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages
to 2.7.5. (AMBARI-24842. BE: Performance Tune Hosts Pages) (mpapirkovskyy)
* AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages
to 2.7.5. License fix. (AMBARI-24876. BE: Performance Tune service Configs
Pages.) (mpapirkovskyy)
* AMBARI-25392. Backport: Performance Tune Hosts and service Configs Pages
to 2.7.5. Test fix. (mpapirkovskyy)
---
.../ambari/server/NullHostNameException.java} | 14 +-
.../ambari/server/agent/stomp/TopologyHolder.java | 26 ++-
.../server/agent/stomp/dto/TopologyCluster.java | 40 +++-
.../server/agent/stomp/dto/TopologyComponent.java | 10 +-
.../server/agent/stomp/dto/TopologyHost.java | 3 -
.../stomp/dto/TopologyUpdateHandlingReport.java | 52 +++++
.../StackAdvisorBlueprintProcessor.java | 5 +-
.../services/stackadvisor/StackAdvisorHelper.java | 77 ++++++-
.../services/stackadvisor/StackAdvisorRequest.java | 32 ++-
.../ComponentLayoutRecommendationCommand.java | 3 +-
.../ConfigurationRecommendationCommand.java | 7 +-
.../stackadvisor/commands/StackAdvisorCommand.java | 88 ++++++--
.../recommendations/RecommendationResponse.java | 39 ++++
.../controller/AmbariManagementControllerImpl.java | 6 +-
.../ambari/server/controller/AmbariServer.java | 2 +-
.../controller/internal/HostResourceProvider.java | 3 +-
.../internal/RecommendationResourceProvider.java | 75 ++++---
.../internal/StackAdvisorResourceProvider.java | 232 ++++++++++++++++++---
.../events/listeners/hosts/HostUpdateListener.java | 12 +-
.../org/apache/ambari/server/state/Cluster.java | 7 +
.../java/org/apache/ambari/server/state/Host.java | 29 +++
.../ambari/server/state/cluster/ClusterImpl.java | 11 +-
.../apache/ambari/server/state/host/HostImpl.java | 65 ++++--
.../topology/STOMPComponentsDeleteHandler.java | 4 +-
.../server/agent/stomp/TopologyClusterTest.java | 186 +++++++++++++++++
.../StackAdvisorBlueprintProcessorTest.java | 13 +-
.../stackadvisor/StackAdvisorHelperTest.java | 149 ++++++++++++-
.../ConfigurationRecommendationCommandTest.java | 5 +-
.../commands/StackAdvisorCommandTest.java | 98 +++++++--
.../server/controller/KerberosHelperTest.java | 11 +-
.../RecommendationResourceProviderTest.java | 167 ++++++++++++++-
.../internal/ValidationResourceProviderTest.java | 4 +-
.../app/controllers/main/service/info/configs.js | 6 +
.../app/mixins/common/configs/enhanced_configs.js | 40 +++-
.../mixins/common/configs/enhanced_configs_test.js | 63 ++++--
35 files changed, 1376 insertions(+), 208 deletions(-)
diff --git
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
b/ambari-server/src/main/java/org/apache/ambari/server/NullHostNameException.java
similarity index 77%
copy from
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
copy to
ambari-server/src/main/java/org/apache/ambari/server/NullHostNameException.java
index 8d02821..346000d 100644
---
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/NullHostNameException.java
@@ -16,14 +16,14 @@
* limitations under the License.
*/
-package org.apache.ambari.server.controller.internal;
+package org.apache.ambari.server;
-import org.junit.Test;
-
-public class RecommendationResourceProviderTest {
-
- @Test
- public void testCreateResources() throws Exception {
+/**
+ * Thrown when an attempt is made to use null as host name
+ */
+public class NullHostNameException extends AmbariException {
+ public NullHostNameException(String message) {
+ super(message);
}
}
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TopologyHolder.java
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TopologyHolder.java
index 7224afc..7c58c1e 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TopologyHolder.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TopologyHolder.java
@@ -29,6 +29,8 @@ import org.apache.ambari.server.ClusterNotFoundException;
import org.apache.ambari.server.agent.stomp.dto.TopologyCluster;
import org.apache.ambari.server.agent.stomp.dto.TopologyComponent;
import org.apache.ambari.server.agent.stomp.dto.TopologyHost;
+import org.apache.ambari.server.agent.stomp.dto.TopologyUpdateHandlingReport;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
import org.apache.ambari.server.controller.AmbariManagementControllerImpl;
import org.apache.ambari.server.events.ClusterComponentsRepoChangedEvent;
import org.apache.ambari.server.events.TopologyAgentUpdateEvent;
@@ -62,6 +64,9 @@ public class TopologyHolder extends
AgentClusterDataHolder<TopologyUpdateEvent>
private Clusters clusters;
@Inject
+ private StackAdvisorHelper stackAdvisorHelper;
+
+ @Inject
public TopologyHolder(AmbariEventPublisher ambariEventPublisher) {
ambariEventPublisher.register(this);
}
@@ -101,6 +106,10 @@ public class TopologyHolder extends
AgentClusterDataHolder<TopologyUpdateEvent>
.filter(h -> hostNames.contains(h.getHostName()))
.map(Host::getHostId)
.collect(Collectors.toSet());
+ Set<String> hostOrderNames = clusterHosts.stream()
+ .filter(h -> hostNames.contains(h.getHostName()))
+ .map(Host::getHostName)
+ .collect(Collectors.toSet());
String serviceName = sch.getServiceName();
String componentName = sch.getServiceComponentName();
StackId stackId = cl.getDesiredStackVersion();
@@ -108,7 +117,7 @@ public class TopologyHolder extends
AgentClusterDataHolder<TopologyUpdateEvent>
TopologyComponent topologyComponent =
TopologyComponent.newBuilder()
.setComponentName(sch.getServiceComponentName())
.setServiceName(sch.getServiceName())
- .setHostIds(hostOrderIds)
+ .setHostIdentifiers(hostOrderIds, hostOrderNames)
.setComponentLevelParams(ambariManagementController.getTopologyComponentLevelParams(cl.getClusterId(),
serviceName,
componentName, cl.getSecurityType()))
.setCommandParams(ambariManagementController.getTopologyCommandParams(cl.getClusterId(),
serviceName, componentName, sch))
@@ -144,7 +153,7 @@ public class TopologyHolder extends
AgentClusterDataHolder<TopologyUpdateEvent>
@Override
protected boolean handleUpdate(TopologyUpdateEvent update) throws
AmbariException {
- boolean changed = false;
+ TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
UpdateEventType eventType = update.getEventType();
for (Map.Entry<String, TopologyCluster> updatedCluster :
update.getClusters().entrySet()) {
String clusterId = updatedCluster.getKey();
@@ -154,25 +163,24 @@ public class TopologyHolder extends
AgentClusterDataHolder<TopologyUpdateEvent>
CollectionUtils.isEmpty(cluster.getTopologyComponents()) &&
CollectionUtils.isEmpty(cluster.getTopologyHosts())) {
getData().getClusters().remove(clusterId);
- changed = true;
+ report.mappingWasChanged();
} else {
- if (getData().getClusters().get(clusterId).update(
+ getData().getClusters().get(clusterId).update(
update.getClusters().get(clusterId).getTopologyComponents(),
update.getClusters().get(clusterId).getTopologyHosts(),
- eventType)) {
- changed = true;
- }
+ eventType, report);
}
} else {
if (eventType.equals(UpdateEventType.UPDATE)) {
getData().getClusters().put(clusterId, cluster);
- changed = true;
+ report.mappingWasChanged();
} else {
throw new ClusterNotFoundException(Long.parseLong(clusterId));
}
}
}
- return changed;
+ stackAdvisorHelper.clearCaches(report.getUpdatedHostNames());
+ return report.wasChanged();
}
private void prepareAgentTopology(TopologyUpdateEvent topologyUpdateEvent) {
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyCluster.java
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyCluster.java
index 2a49f43..272f1e1 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyCluster.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyCluster.java
@@ -21,6 +21,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import org.apache.ambari.server.NullHostNameException;
import org.apache.ambari.server.events.UpdateEventType;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.SetUtils;
@@ -44,9 +45,8 @@ public class TopologyCluster {
this.topologyHosts = topologyHosts;
}
- public boolean update(Set<TopologyComponent> componentsToUpdate,
Set<TopologyHost> hostsToUpdate,
- UpdateEventType eventType) {
- boolean changed = false;
+ public void update(Set<TopologyComponent> componentsToUpdate,
Set<TopologyHost> hostsToUpdate,
+ UpdateEventType eventType, TopologyUpdateHandlingReport
report) throws NullHostNameException {
for (TopologyComponent componentToUpdate : componentsToUpdate) {
boolean isPresent = false;
for (Iterator<TopologyComponent> iter =
getTopologyComponents().iterator(); iter.hasNext() && !isPresent; ) {
@@ -55,19 +55,32 @@ public class TopologyCluster {
if (eventType.equals(UpdateEventType.DELETE)) {
if (SetUtils.isEqualSet(existsComponent.getHostIds(),
componentToUpdate.getHostIds())) {
iter.remove();
- changed = true;
+ report.mappingWasChanged();
+ report.addHostsNames(componentToUpdate.getHostNames());
} else {
- changed |= existsComponent.removeComponent(componentToUpdate);
+ if (existsComponent.removeComponent(componentToUpdate)) {
+ report.mappingWasChanged();
+ report.addHostsNames(componentToUpdate.getHostNames());
+ }
}
} else {
- changed |= existsComponent.updateComponent(componentToUpdate);
+ Set<String> preExistNames = new
HashSet<>(existsComponent.getHostNames());
+ if (existsComponent.updateComponent(componentToUpdate)) {
+ report.mappingWasChanged();
+
+ // calc changed hosts
+ Set<String> namesToUpdate = new
HashSet<>(componentToUpdate.getHostNames());
+ namesToUpdate.removeAll(preExistNames);
+ report.addHostsNames(namesToUpdate);
+ }
}
isPresent = true;
}
}
if (!isPresent && eventType.equals(UpdateEventType.UPDATE)) {
getTopologyComponents().add(componentToUpdate);
- changed = true;
+ report.mappingWasChanged();
+ report.addHostsNames(componentToUpdate.getHostNames());
}
}
for (TopologyHost hostToUpdate : hostsToUpdate) {
@@ -77,19 +90,24 @@ public class TopologyCluster {
if (existsHost.equals(hostToUpdate)) {
if (eventType.equals(UpdateEventType.DELETE)) {
iter.remove();
- changed = true;
+ report.mappingWasChanged();
+ report.addHostName(existsHost.getHostName());
} else {
- changed |= existsHost.updateHost(hostToUpdate);
+ if (existsHost.updateHost(hostToUpdate)) {
+ report.mappingWasChanged();
+ report.addHostName(existsHost.getHostName());
+ report.addHostName(hostToUpdate.getHostName());
+ }
}
isPresent = true;
}
}
if (!isPresent && eventType.equals(UpdateEventType.UPDATE)) {
getTopologyHosts().add(hostToUpdate);
- changed = true;
+ report.mappingWasChanged();
+ report.addHostName(hostToUpdate.getHostName());
}
}
- return changed;
}
public Set<TopologyComponent> getTopologyComponents() {
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyComponent.java
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyComponent.java
index ef0871f..1351e79 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyComponent.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyComponent.java
@@ -75,12 +75,8 @@ public class TopologyComponent {
return this;
}
- public Builder setHostIds(Set<Long> hostIds) {
+ public Builder setHostIdentifiers(Set<Long> hostIds, Set<String>
hostNames) {
TopologyComponent.this.setHostIds(hostIds);
- return this;
- }
-
- public Builder setHostNames(Set<String> hostNames) {
TopologyComponent.this.setHostNames(hostNames);
return this;
}
@@ -188,8 +184,8 @@ public class TopologyComponent {
.setDisplayName(getDisplayName())
.setServiceName(getServiceName())
.setComponentLevelParams(getComponentLevelParams() == null ? null :
new TreeMap<>(getComponentLevelParams()))
- .setHostIds(getHostIds() == null ? null : new HashSet<>(getHostIds()))
- .setHostNames(getHostNames() == null ? null : new
HashSet<>(getHostNames()))
+ .setHostIdentifiers(getHostIds() == null ? null : new
HashSet<>(getHostIds()),
+ getHostNames() == null ? null : new HashSet<>(getHostNames()))
.setPublicHostNames(getPublicHostNames() == null ? null : new
HashSet<>(getPublicHostNames()))
.setCommandParams(getCommandParams() == null ? null : new
TreeMap<>(getCommandParams()))
.build();
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyHost.java
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyHost.java
index 8b1d43d..45ac78f 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyHost.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyHost.java
@@ -28,9 +28,6 @@ public class TopologyHost {
private String rackName;
private String ipv4;
- public TopologyHost() {
- }
-
public TopologyHost(Long hostId, String hostName) {
this.hostId = hostId;
this.hostName = hostName;
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyUpdateHandlingReport.java
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyUpdateHandlingReport.java
new file mode 100644
index 0000000..3fbac25
--- /dev/null
+++
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyUpdateHandlingReport.java
@@ -0,0 +1,52 @@
+/**
+ * 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.agent.stomp.dto;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.ambari.server.NullHostNameException;
+
+public class TopologyUpdateHandlingReport {
+ private Set<String> updatedHostNames = new HashSet<>();
+ private boolean mappingChanged = false;
+
+ public boolean wasChanged(){
+ return mappingChanged || !updatedHostNames.isEmpty();
+ }
+
+ public Set<String> getUpdatedHostNames() {
+ return updatedHostNames;
+ }
+
+ public void addHostName(String updatedHostName) throws NullHostNameException
{
+ if (updatedHostName == null) {
+ throw new NullHostNameException("Host name could not be a null");
+ }
+ this.updatedHostNames.add(updatedHostName);
+ }
+
+ public void addHostsNames(Set<String> updatedHostNames) {
+ this.updatedHostNames.addAll(updatedHostNames);
+ }
+
+ public void mappingWasChanged() {
+ this.mappingChanged = true;
+ }
+}
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessor.java
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessor.java
index 273c0ff..6084711 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessor.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessor.java
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.apache.ambari.server.AmbariException;
import
org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestType;
import
org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
import
org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse.BlueprintConfigurations;
@@ -81,7 +82,7 @@ public class StackAdvisorBlueprintProcessor {
try {
RecommendationResponse response = stackAdvisorHelper.recommend(request);
addAdvisedConfigurationsToTopology(response, clusterTopology,
userProvidedConfigurations);
- } catch (StackAdvisorException e) {
+ } catch (StackAdvisorException | AmbariException e) {
throw new ConfigurationTopologyException(RECOMMENDATION_FAILED, e);
} catch (IllegalArgumentException e) {
throw new ConfigurationTopologyException(INVALID_RESPONSE, e);
@@ -93,7 +94,7 @@ public class StackAdvisorBlueprintProcessor {
Map<String, Set<String>> hgComponentsMap =
gatherHostGroupComponents(clusterTopology);
Map<String, Set<String>> hgHostsMap =
gatherHostGroupBindings(clusterTopology);
Map<String, Set<String>> componentHostsMap =
gatherComponentsHostsMap(hgComponentsMap,
- hgHostsMap);
+ hgHostsMap);
return StackAdvisorRequest.StackAdvisorRequestBuilder
.forStack(stack.getName(), stack.getVersion())
.forServices(new
ArrayList<>(clusterTopology.getBlueprint().getServices()))
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
index f6770bfa..51e1899 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
@@ -20,6 +20,12 @@ package org.apache.ambari.server.api.services.stackadvisor;
import java.io.File;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
@@ -38,7 +44,9 @@ import
org.apache.ambari.server.controller.internal.AmbariServerConfigurationHan
import org.apache.ambari.server.state.ServiceInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.codehaus.jackson.JsonNode;
+import com.google.gson.Gson;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -54,14 +62,19 @@ public class StackAdvisorHelper {
public static String pythonStackAdvisorScript;
private final AmbariMetaInfo metaInfo;
private final AmbariServerConfigurationHandler
ambariServerConfigurationHandler;
+ private final Gson gson;
/* Monotonically increasing requestid */
private int requestId = 0;
private StackAdvisorRunner saRunner;
+ private Map<String, JsonNode> hostInfoCache = new ConcurrentHashMap<>();
+ private Map<String, RecommendationResponse> configsRecommendationResponse =
new ConcurrentHashMap<>();
+
+
@Inject
- public StackAdvisorHelper(Configuration conf, StackAdvisorRunner saRunner,
- AmbariMetaInfo metaInfo,
AmbariServerConfigurationHandler ambariServerConfigurationHandler) throws
IOException {
+ public StackAdvisorHelper(Configuration conf, StackAdvisorRunner saRunner,
AmbariMetaInfo metaInfo,
+ AmbariServerConfigurationHandler
ambariServerConfigurationHandler, Gson gson) throws IOException {
this.recommendationsDir = conf.getRecommendationsDir();
this.recommendationsArtifactsLifetime =
conf.getRecommendationsArtifactsLifetime();
this.recommendationsArtifactsRolloverMax =
conf.getRecommendationsArtifactsRolloverMax();
@@ -70,6 +83,7 @@ public class StackAdvisorHelper {
this.saRunner = saRunner;
this.metaInfo = metaInfo;
this.ambariServerConfigurationHandler = ambariServerConfigurationHandler;
+ this.gson = gson;
}
/**
@@ -122,7 +136,7 @@ public class StackAdvisorHelper {
* @throws StackAdvisorException in case of stack advisor script errors
*/
public synchronized RecommendationResponse recommend(StackAdvisorRequest
request)
- throws StackAdvisorException {
+ throws StackAdvisorException, AmbariException {
requestId = generateRequestId();
// TODO, need to pass the service Name that was modified.
@@ -132,7 +146,40 @@ public class StackAdvisorHelper {
ServiceInfo.ServiceAdvisorType serviceAdvisorType =
getServiceAdvisorType(request.getStackName(), request.getStackVersion(),
serviceName);
StackAdvisorCommand<RecommendationResponse> command =
createRecommendationCommand(serviceName, request);
- return command.invoke(request, serviceAdvisorType);
+ StackAdvisorRequestType requestType = request.getRequestType();
+ RecommendationResponse response = null;
+ if (requestType == StackAdvisorRequestType.CONFIGURATIONS) {
+ String hash = getHash(request);
+ LOG.info(String.format("Arrived configuration stack advisor request with
hash: %s, service: %s", hash, request.getServiceName()));
+ response = configsRecommendationResponse.computeIfAbsent(hash, h -> {
+ try {
+ LOG.info(String.format("Invoking configuration stack advisor request
with hash: %s, service: %s", hash, request.getServiceName()));
+ return command.invoke(request, serviceAdvisorType);
+ } catch (StackAdvisorException e) {
+ return null;
+ }
+ });
+ }
+
+ return response == null ? command.invoke(request, serviceAdvisorType) :
response;
+ }
+
+ protected String getHash(StackAdvisorRequest request) {
+ String json = gson.toJson(request);
+ String generatedPassword = null;
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-512");
+ md.update("".getBytes("UTF-8"));
+ byte[] bytes = md.digest(json.getBytes("UTF-8"));
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
+ }
+ generatedPassword = sb.toString();
+ } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ return generatedPassword;
}
StackAdvisorCommand<RecommendationResponse>
createRecommendationCommand(String serviceName, StackAdvisorRequest request)
throws StackAdvisorException {
@@ -145,13 +192,13 @@ public class StackAdvisorHelper {
requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
} else if (requestType == StackAdvisorRequestType.CONFIGURATIONS) {
command = new
ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS,
recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
- requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+ requestId, saRunner, metaInfo, ambariServerConfigurationHandler,
hostInfoCache);
} else if (requestType == StackAdvisorRequestType.SSO_CONFIGURATIONS) {
command = new
ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS_FOR_SSO,
recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
- requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+ requestId, saRunner, metaInfo, ambariServerConfigurationHandler,
null);
} else if (requestType == StackAdvisorRequestType.KERBEROS_CONFIGURATIONS)
{
command = new
ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS_FOR_KERBEROS,
recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
- requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+ requestId, saRunner, metaInfo, ambariServerConfigurationHandler,
null);
} else if (requestType ==
StackAdvisorRequestType.CONFIGURATION_DEPENDENCIES) {
command = new
ConfigurationDependenciesRecommendationCommand(recommendationsDir,
recommendationsArtifactsLifetime, serviceAdvisorType,
requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
@@ -192,4 +239,20 @@ public class StackAdvisorHelper {
}
+ public void clearCaches(String hostName) {
+ configsRecommendationResponse.clear();
+ hostInfoCache.remove(hostName);
+ LOG.info("Clear stack advisor caches, host: " + hostName);
+ }
+
+ public void clearCaches(Set<String> hostNames) {
+ if (hostNames != null && !hostNames.isEmpty()) {
+ configsRecommendationResponse.clear();
+ for (String hostName : hostNames) {
+ hostInfoCache.remove(hostName);
+ }
+ }
+ LOG.info("Clear stack advisor caches, hosts: " + hostNames.toString());
+ }
+
}
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
index 83a9367..6733a32 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
@@ -38,6 +38,8 @@ import com.google.common.base.Preconditions;
*/
public class StackAdvisorRequest {
+ private Long clusterId;
+ private String serviceName;
private String stackName;
private String stackVersion;
private StackAdvisorRequestType requestType;
@@ -52,6 +54,7 @@ public class StackAdvisorRequest {
private Map<String, String> userContext = new HashMap<>();
private Map<String, Object> ldapConfig = new HashMap<>();
private Boolean gplLicenseAccepted;
+ private Boolean configsResponse = false;
public String getStackName() {
return stackName;
@@ -123,6 +126,14 @@ public class StackAdvisorRequest {
this.configGroups = configGroups;
}
+ public Long getClusterId() {
+ return clusterId;
+ }
+
+ public String getServiceName() {
+ return serviceName;
+ }
+
/**
* @return true if GPL license is accepted, false otherwise
*/
@@ -130,6 +141,10 @@ public class StackAdvisorRequest {
return gplLicenseAccepted;
}
+ public Boolean getConfigsResponse() {
+ return configsResponse;
+ }
+
private StackAdvisorRequest(String stackName, String stackVersion) {
this.stackName = stackName;
this.stackVersion = stackVersion;
@@ -197,7 +212,7 @@ public class StackAdvisorRequest {
}
public StackAdvisorRequestBuilder withConfigGroups(
- Set<RecommendationResponse.ConfigGroup> configGroups) {
+ Set<RecommendationResponse.ConfigGroup> configGroups) {
this.instance.configGroups = configGroups;
return this;
}
@@ -219,6 +234,21 @@ public class StackAdvisorRequest {
return this;
}
+ public StackAdvisorRequestBuilder withClusterId(Long clusterId) {
+ this.instance.clusterId = clusterId;
+ return this;
+ }
+
+ public StackAdvisorRequestBuilder withServiceName(String serviceName) {
+ this.instance.serviceName = serviceName;
+ return this;
+ }
+
+ public StackAdvisorRequestBuilder withConfigsResponse(
+ Boolean configsResponse) {
+ this.instance.configsResponse = configsResponse;
+ return this;
+ }
public StackAdvisorRequest build() {
return this.instance;
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ComponentLayoutRecommendationCommand.java
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ComponentLayoutRecommendationCommand.java
index e38a48d..371727a 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ComponentLayoutRecommendationCommand.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ComponentLayoutRecommendationCommand.java
@@ -42,7 +42,8 @@ public class ComponentLayoutRecommendationCommand extends
StackAdvisorRunner saRunner,
AmbariMetaInfo metaInfo,
AmbariServerConfigurationHandler
ambariServerConfigurationHandler) {
- super(recommendationsDir, recommendationsArtifactsLifetime,
serviceAdvisorType, requestId, saRunner, metaInfo,
ambariServerConfigurationHandler);
+ super(recommendationsDir, recommendationsArtifactsLifetime,
serviceAdvisorType, requestId, saRunner, metaInfo,
+ ambariServerConfigurationHandler);
}
@Override
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommand.java
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommand.java
index 56cf957..d140353 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommand.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommand.java
@@ -35,6 +35,7 @@ import
org.apache.ambari.server.api.services.stackadvisor.recommendations.Recomm
import
org.apache.ambari.server.controller.internal.AmbariServerConfigurationHandler;
import org.apache.ambari.server.state.ServiceInfo;
import org.apache.commons.collections.CollectionUtils;
+import org.codehaus.jackson.JsonNode;
/**
* {@link
org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommand}
implementation for
@@ -57,8 +58,10 @@ public class ConfigurationRecommendationCommand extends
StackAdvisorCommand<Reco
int requestId,
StackAdvisorRunner saRunner,
AmbariMetaInfo metaInfo,
- AmbariServerConfigurationHandler
ambariServerConfigurationHandler) {
- super(recommendationsDir, recommendationsArtifactsLifetime,
serviceAdvisorType, requestId, saRunner, metaInfo,
ambariServerConfigurationHandler);
+ AmbariServerConfigurationHandler
ambariServerConfigurationHandler,
+ Map<String, JsonNode>
hostInfoCache) {
+ super(recommendationsDir, recommendationsArtifactsLifetime,
serviceAdvisorType, requestId, saRunner, metaInfo,
+ ambariServerConfigurationHandler, hostInfoCache);
this.commandType = commandType;
}
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
index 5b0097b..eb0025e 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java
@@ -59,6 +59,7 @@ import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.node.ArrayNode;
+import org.codehaus.jackson.node.JsonNodeFactory;
import org.codehaus.jackson.node.ObjectNode;
import org.codehaus.jackson.node.TextNode;
import org.slf4j.Logger;
@@ -104,6 +105,8 @@ public abstract class StackAdvisorCommand<T extends
StackAdvisorResponse> extend
private static final String AMBARI_SERVER_PROPERTIES_PROPERTY =
"ambari-server-properties";
private static final String AMBARI_SERVER_CONFIGURATIONS_PROPERTY =
"ambari-server-configuration";
+ private final Map<String, JsonNode> hostInfoCache;
+
private File recommendationsDir;
private String recommendationsArtifactsLifetime;
private ServiceInfo.ServiceAdvisorType serviceAdvisorType;
@@ -120,7 +123,8 @@ public abstract class StackAdvisorCommand<T extends
StackAdvisorResponse> extend
@SuppressWarnings("unchecked")
public StackAdvisorCommand(File recommendationsDir, String
recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType
serviceAdvisorType, int requestId,
- StackAdvisorRunner saRunner, AmbariMetaInfo
metaInfo, AmbariServerConfigurationHandler ambariServerConfigurationHandler) {
+ StackAdvisorRunner saRunner, AmbariMetaInfo
metaInfo, AmbariServerConfigurationHandler ambariServerConfigurationHandler,
+ Map<String, JsonNode> hostInfoCache) {
this.type = (Class<T>) ((ParameterizedType)
getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
@@ -134,6 +138,12 @@ public abstract class StackAdvisorCommand<T extends
StackAdvisorResponse> extend
this.saRunner = saRunner;
this.metaInfo = metaInfo;
this.ambariServerConfigurationHandler = ambariServerConfigurationHandler;
+ this.hostInfoCache = hostInfoCache;
+ }
+ public StackAdvisorCommand(File recommendationsDir, String
recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType
serviceAdvisorType, int requestId,
+ StackAdvisorRunner saRunner, AmbariMetaInfo
metaInfo, AmbariServerConfigurationHandler ambariServerConfigurationHandler) {
+ this(recommendationsDir, recommendationsArtifactsLifetime,
serviceAdvisorType, requestId, saRunner, metaInfo,
+ ambariServerConfigurationHandler, null);
}
protected abstract StackAdvisorCommandType getCommandType();
@@ -383,22 +393,71 @@ public abstract class StackAdvisorCommand<T extends
StackAdvisorResponse> extend
}
String getHostsInformation(StackAdvisorRequest request) throws
StackAdvisorException {
- String hostsURI = String.format(GET_HOSTS_INFO_URI,
request.getHostsCommaSeparated());
-
- Response response = handleRequest(null, null, new LocalUriInfo(hostsURI),
Request.Type.GET,
- createHostResource());
+ List<String> hostNames = new ArrayList<>(request.getHosts());
+
+ // retrieve cached info
+ List<JsonNode> resultInfos = new ArrayList<>();
+ if (hostInfoCache != null && !hostInfoCache.isEmpty()) {
+ Iterator<String> hostNamesIterator = hostNames.iterator();
+ while(hostNamesIterator.hasNext()) {
+ String hostName = hostNamesIterator.next();
+ JsonNode node = hostInfoCache.get(hostName);
+ if (node != null) {
+ resultInfos.add(node);
+ hostNamesIterator.remove();
+ }
+ }
+ }
+ String hostsJSON = null;
+
+ // get hosts info for not cached hosts only
+ if (!hostNames.isEmpty()) {
+ LOG.info(String.format("Fire host info request for hosts: " +
hostNames.toString()));
+ String hostsURI = String.format(GET_HOSTS_INFO_URI, String.join(",",
hostNames));
+
+ Response response = handleRequest(null, null, new
LocalUriInfo(hostsURI), Request.Type.GET,
+ createHostResource());
+
+ if (response.getStatus() != Status.OK.getStatusCode()) {
+ String message = String.format(
+ "Error occured during hosts information retrieving, status=%s,
response=%s",
+ response.getStatus(), (String) response.getEntity());
+ LOG.warn(message);
+ throw new StackAdvisorException(message);
+ }
- if (response.getStatus() != Status.OK.getStatusCode()) {
- String message = String.format(
- "Error occured during hosts information retrieving, status=%s,
response=%s",
- response.getStatus(), (String) response.getEntity());
- LOG.warn(message);
- throw new StackAdvisorException(message);
+ hostsJSON = (String) response.getEntity();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Hosts information: {}", hostsJSON);
+ }
}
- String hostsJSON = (String) response.getEntity();
- if (LOG.isDebugEnabled()) {
- LOG.debug("Hosts information: {}", hostsJSON);
+ // when cache is used we should merge cached info with just got
+ if (hostInfoCache != null) {
+ if (hostsJSON != null && !hostsJSON.isEmpty()) {
+ try {
+ JsonNode root = mapper.readTree(hostsJSON);
+ Iterator<JsonNode> iterator = root.get("items").getElements();
+ while (iterator.hasNext()) {
+ JsonNode next = iterator.next();
+ String hostName =
next.get("Hosts").get("host_name").getTextValue();
+ hostInfoCache.put(hostName, next);
+ resultInfos.add(next);
+ }
+ } catch (IOException e) {
+ throw new StackAdvisorException("Error occured during parsing result
host infos", e);
+ }
+ }
+
+ String fullHostsURI = String.format(GET_HOSTS_INFO_URI,
request.getHostsCommaSeparated());
+ JsonNodeFactory f = JsonNodeFactory.instance;
+ ObjectNode resultRoot = f.objectNode();
+ resultRoot.put("href", fullHostsURI);
+ ArrayNode resultArray = resultRoot.putArray("items");
+ resultArray.addAll(resultInfos);
+
+ hostsJSON = resultRoot.toString();
+
}
Collection<String> unregistered = getUnregisteredHosts(hostsJSON,
request.getHosts());
@@ -415,7 +474,6 @@ public abstract class StackAdvisorCommand<T extends
StackAdvisorResponse> extend
@SuppressWarnings("unchecked")
private Collection<String> getUnregisteredHosts(String hostsJSON,
List<String> hosts)
throws StackAdvisorException {
- ObjectMapper mapper = new ObjectMapper();
List<String> registeredHosts = new ArrayList<>();
try {
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/recommendations/RecommendationResponse.java
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/recommendations/RecommendationResponse.java
index 0743e6c..1374995 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/recommendations/RecommendationResponse.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/recommendations/RecommendationResponse.java
@@ -163,6 +163,24 @@ public class RecommendationResponse extends
StackAdvisorResponse {
public void setPropertyAttributes(Map<String, ValueAttributesInfo>
propertyAttributes) {
this.propertyAttributes = propertyAttributes;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BlueprintConfigurations that = (BlueprintConfigurations) o;
+
+ if (properties != null ? !properties.equals(that.properties) :
that.properties != null) return false;
+ return propertyAttributes != null ?
propertyAttributes.equals(that.propertyAttributes) : that.propertyAttributes ==
null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = properties != null ? properties.hashCode() : 0;
+ result = 31 * result + (propertyAttributes != null ?
propertyAttributes.hashCode() : 0);
+ return result;
+ }
}
public static class HostGroup {
@@ -268,6 +286,27 @@ public class RecommendationResponse extends
StackAdvisorResponse {
public void setDependentConfigurations(Map<String,
BlueprintConfigurations> dependentConfigurations) {
this.dependentConfigurations = dependentConfigurations;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ConfigGroup that = (ConfigGroup) o;
+
+ if (hosts != null ? !hosts.equals(that.hosts) : that.hosts != null)
return false;
+ if (configurations != null ? !configurations.equals(that.configurations)
: that.configurations != null)
+ return false;
+ return dependentConfigurations != null ?
dependentConfigurations.equals(that.dependentConfigurations) :
that.dependentConfigurations == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = hosts != null ? hosts.hashCode() : 0;
+ result = 31 * result + (configurations != null ?
configurations.hashCode() : 0);
+ result = 31 * result + (dependentConfigurations != null ?
dependentConfigurations.hashCode() : 0);
+ return result;
+ }
}
}
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 31cc39a..bdfdac5 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
@@ -116,6 +116,7 @@ import
org.apache.ambari.server.agent.stomp.dto.MetadataCluster;
import org.apache.ambari.server.agent.stomp.dto.MetadataServiceInfo;
import org.apache.ambari.server.agent.stomp.dto.TopologyCluster;
import org.apache.ambari.server.agent.stomp.dto.TopologyComponent;
+import org.apache.ambari.server.agent.stomp.dto.TopologyUpdateHandlingReport;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.api.services.LoggingService;
import org.apache.ambari.server.configuration.Configuration;
@@ -790,8 +791,7 @@ public class AmbariManagementControllerImpl implements
AmbariManagementControlle
.setComponentName(sch.getServiceComponentName())
.setServiceName(sch.getServiceName())
.setDisplayName(sc.getDisplayName())
- .setHostIds(hostIds)
- .setHostNames(hostNames)
+ .setHostIdentifiers(hostIds, hostNames)
.setPublicHostNames(publicHostNames)
.setComponentLevelParams(getTopologyComponentLevelParams(cluster.getClusterId(),
serviceName, componentName,
cluster.getSecurityType()))
@@ -806,7 +806,7 @@ public class AmbariManagementControllerImpl implements
AmbariManagementControlle
Set<TopologyComponent> newComponents = new HashSet<>();
newComponents.add(newComponent);
topologyUpdates.get(clusterId).update(newComponents,
Collections.emptySet(),
- UpdateEventType.UPDATE);
+ UpdateEventType.UPDATE, new TopologyUpdateHandlingReport());
} else {
topologyUpdates.get(clusterId).addTopologyComponent(newComponent);
}
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
index b7f44ff..b8f440a 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
@@ -917,7 +917,7 @@ public class AmbariServer {
KeyService.init(injector.getInstance(PersistKeyValueImpl.class));
BootStrapResource.init(injector.getInstance(BootStrapImpl.class));
StackAdvisorResourceProvider.init(injector.getInstance(StackAdvisorHelper.class),
- injector.getInstance(Configuration.class));
+ injector.getInstance(Configuration.class),
injector.getInstance(Clusters.class),
injector.getInstance(AmbariMetaInfo.class));
StageUtils.setGson(injector.getInstance(Gson.class));
StageUtils.setTopologyManager(injector.getInstance(TopologyManager.class));
StageUtils.setConfiguration(injector.getInstance(Configuration.class));
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 9fd135d..059316f 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
@@ -827,7 +827,6 @@ public class HostResourceProvider extends
AbstractControllerResourceProvider {
if (LOG.isDebugEnabled()) {
LOG.debug("Received an updateHost request, hostname={}, request={}",
request.getHostname(), request);
}
- TopologyHost topologyHost = new TopologyHost();
Host host = clusters.getHost(request.getHostname());
@@ -835,7 +834,7 @@ public class HostResourceProvider extends
AbstractControllerResourceProvider {
Cluster cluster = clusters.getCluster(clusterName);
Long clusterId = cluster.getClusterId();
Long resourceId = cluster.getResourceId();
- topologyHost.setHostId(host.getHostId());
+ TopologyHost topologyHost = new TopologyHost(host.getHostId(),
host.getHostName());
try {
// The below method call throws an exception when trying to create a
duplicate mapping in the clusterhostmapping
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java
index dcc6cb6..50aa092 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RecommendationResourceProvider.java
@@ -56,6 +56,10 @@ public class RecommendationResourceProvider extends
StackAdvisorResourceProvider
protected static final String RECOMMENDATION_ID_PROPERTY_ID =
PropertyHelper.getPropertyId(
"Recommendation", "id");
+ protected static final String CLUSTER_ID_PROPERTY_ID = "clusterId";
+ protected static final String SERVICE_NAME_PROPERTY_ID = "serviceName";
+ protected static final String AUTO_COMPLETE_PROPERTY_ID = "autoComplete";
+ protected static final String CONFIGS_RESPONSE_PROPERTY_ID =
"configsResponse";
protected static final String HOSTS_PROPERTY_ID = "hosts";
protected static final String SERVICES_PROPERTY_ID = "services";
protected static final String RECOMMEND_PROPERTY_ID = "recommend";
@@ -105,6 +109,10 @@ public class RecommendationResourceProvider extends
StackAdvisorResourceProvider
STACK_NAME_PROPERTY_ID,
STACK_VERSION_PROPERTY_ID,
RECOMMEND_PROPERTY_ID,
+ CLUSTER_ID_PROPERTY_ID,
+ SERVICE_NAME_PROPERTY_ID,
+ AUTO_COMPLETE_PROPERTY_ID,
+ CONFIGS_RESPONSE_PROPERTY_ID,
HOSTS_PROPERTY_ID,
SERVICES_PROPERTY_ID,
CONFIG_GROUPS_PROPERTY_ID,
@@ -146,7 +154,7 @@ public class RecommendationResourceProvider extends
StackAdvisorResourceProvider
} catch (StackAdvisorRequestException e) {
LOG.warn("Error occured during recommendation", e);
throw new IllegalArgumentException(e.getMessage(), e);
- } catch (StackAdvisorException e) {
+ } catch (StackAdvisorException | AmbariException e) {
LOG.warn("Error occured during recommendation", e);
throw new SystemException(e.getMessage(), e);
}
@@ -156,43 +164,46 @@ public class RecommendationResourceProvider extends
StackAdvisorResourceProvider
public Resource invoke() throws AmbariException {
Resource resource = new ResourceImpl(Resource.Type.Recommendation);
- setResourceProperty(resource, RECOMMENDATION_ID_PROPERTY_ID,
response.getId(), getPropertyIds());
- setResourceProperty(resource, STACK_NAME_PROPERTY_ID,
response.getVersion().getStackName(),
- getPropertyIds());
- setResourceProperty(resource, STACK_VERSION_PROPERTY_ID,
response.getVersion()
- .getStackVersion(), getPropertyIds());
- setResourceProperty(resource, HOSTS_PROPERTY_ID, response.getHosts(),
getPropertyIds());
- setResourceProperty(resource, SERVICES_PROPERTY_ID,
response.getServices(),
- getPropertyIds());
+ if (!recommendationRequest.getConfigsResponse()) {
+ setResourceProperty(resource, RECOMMENDATION_ID_PROPERTY_ID,
response.getId(), getPropertyIds());
+ setResourceProperty(resource, STACK_NAME_PROPERTY_ID,
response.getVersion().getStackName(),
+ getPropertyIds());
+ setResourceProperty(resource, STACK_VERSION_PROPERTY_ID,
response.getVersion()
+ .getStackVersion(), getPropertyIds());
+ setResourceProperty(resource, HOSTS_PROPERTY_ID,
response.getHosts(), getPropertyIds());
+ setResourceProperty(resource, SERVICES_PROPERTY_ID,
response.getServices(),
+ getPropertyIds());
+ }
setResourceProperty(resource, CONFIG_GROUPS_PROPERTY_ID,
response.getRecommendations().getConfigGroups(), getPropertyIds());
setResourceProperty(resource, BLUEPRINT_CONFIGURATIONS_PROPERTY_ID,
response
.getRecommendations().getBlueprint().getConfigurations(),
getPropertyIds());
- Set<HostGroup> hostGroups =
response.getRecommendations().getBlueprint().getHostGroups();
- List<Map<String, Object>> listGroupProps = new ArrayList<>();
- for (HostGroup hostGroup : hostGroups) {
- Map<String, Object> mapGroupProps = new HashMap<>();
- mapGroupProps.put(BLUEPRINT_HOST_GROUPS_NAME_PROPERTY_ID,
hostGroup.getName());
- mapGroupProps
- .put(BLUEPRINT_HOST_GROUPS_COMPONENTS_PROPERTY_ID,
hostGroup.getComponents());
- listGroupProps.add(mapGroupProps);
- }
- setResourceProperty(resource, BLUEPRINT_HOST_GROUPS_PROPERTY_ID,
listGroupProps,
- getPropertyIds());
-
- Set<BindingHostGroup> bindingHostGroups = response.getRecommendations()
- .getBlueprintClusterBinding().getHostGroups();
- List<Map<String, Object>> listBindingGroupProps = new ArrayList<>();
- for (BindingHostGroup hostGroup : bindingHostGroups) {
- Map<String, Object> mapGroupProps = new HashMap<>();
- mapGroupProps.put(BINDING_HOST_GROUPS_NAME_PROPERTY_ID,
hostGroup.getName());
- mapGroupProps.put(BINDING_HOST_GROUPS_HOSTS_PROPERTY_ID,
hostGroup.getHosts());
- listBindingGroupProps.add(mapGroupProps);
+ if (!recommendationRequest.getConfigsResponse()) {
+ Set<HostGroup> hostGroups =
response.getRecommendations().getBlueprint().getHostGroups();
+ List<Map<String, Object>> listGroupProps = new ArrayList<>();
+ for (HostGroup hostGroup : hostGroups) {
+ Map<String, Object> mapGroupProps = new HashMap<>();
+ mapGroupProps.put(BLUEPRINT_HOST_GROUPS_NAME_PROPERTY_ID,
hostGroup.getName());
+ mapGroupProps
+ .put(BLUEPRINT_HOST_GROUPS_COMPONENTS_PROPERTY_ID,
hostGroup.getComponents());
+ listGroupProps.add(mapGroupProps);
+ }
+ setResourceProperty(resource, BLUEPRINT_HOST_GROUPS_PROPERTY_ID,
listGroupProps,
+ getPropertyIds());
+
+ Set<BindingHostGroup> bindingHostGroups =
response.getRecommendations()
+ .getBlueprintClusterBinding().getHostGroups();
+ List<Map<String, Object>> listBindingGroupProps = new ArrayList<>();
+ for (BindingHostGroup hostGroup : bindingHostGroups) {
+ Map<String, Object> mapGroupProps = new HashMap<>();
+ mapGroupProps.put(BINDING_HOST_GROUPS_NAME_PROPERTY_ID,
hostGroup.getName());
+ mapGroupProps.put(BINDING_HOST_GROUPS_HOSTS_PROPERTY_ID,
hostGroup.getHosts());
+ listBindingGroupProps.add(mapGroupProps);
+ }
+ setResourceProperty(resource, BINDING_HOST_GROUPS_PROPERTY_ID,
listBindingGroupProps,
+ getPropertyIds());
}
- setResourceProperty(resource, BINDING_HOST_GROUPS_PROPERTY_ID,
listBindingGroupProps,
- getPropertyIds());
-
return resource;
}
});
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
index cf716ea..0dd48e7 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProvider.java
@@ -18,6 +18,7 @@
package org.apache.ambari.server.controller.internal;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -26,11 +27,14 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
import
org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestBuilder;
@@ -43,6 +47,16 @@ import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.Resource.Type;
import org.apache.ambari.server.controller.utilities.PropertyHelper;
import org.apache.ambari.server.state.ChangedConfigInfo;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Config;
+import org.apache.ambari.server.state.DesiredConfig;
+import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.ServiceInfo;
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.configgroup.ConfigGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -60,6 +74,12 @@ public abstract class StackAdvisorResourceProvider extends
ReadOnlyResourceProvi
protected static final String STACK_VERSION_PROPERTY_ID =
PropertyHelper.getPropertyId(
"Versions", "stack_version");
+
+ private static final String CLUSTER_ID_PROPERTY = "clusterId";
+ private static final String SERVICE_NAME_PROPERTY = "serviceName";
+ private static final String AUTO_COMPLETE_PROPERTY = "autoComplete";
+ private static final String CONFIGS_RESPONSE_PROPERTY = "configsResponse";
+ private static final String CONFIG_GROUPS_GROUP_ID_PROPERTY = "group_id";
private static final String HOST_PROPERTY = "hosts";
private static final String SERVICES_PROPERTY = "services";
@@ -84,14 +104,19 @@ public abstract class StackAdvisorResourceProvider extends
ReadOnlyResourceProvi
private static final String CONFIG_GROUPS_HOSTS_PROPERTY = "hosts";
protected static StackAdvisorHelper saHelper;
- protected static Configuration configuration;
+ private static Configuration configuration;
+ private static Clusters clusters;
+ private static AmbariMetaInfo ambariMetaInfo;
protected static final String USER_CONTEXT_OPERATION_PROPERTY =
"user_context/operation";
protected static final String USER_CONTEXT_OPERATION_DETAILS_PROPERTY =
"user_context/operation_details";
@Inject
- public static void init(StackAdvisorHelper instance, Configuration
serverConfig) {
+ public static void init(StackAdvisorHelper instance, Configuration
serverConfig, Clusters clusters,
+ AmbariMetaInfo ambariMetaInfo) {
saHelper = instance;
configuration = serverConfig;
+ StackAdvisorResourceProvider.clusters = clusters;
+ StackAdvisorResourceProvider.ambariMetaInfo = ambariMetaInfo;
}
protected StackAdvisorResourceProvider(Resource.Type type, Set<String>
propertyIds, Map<Type, String> keyPropertyIds,
@@ -104,46 +129,84 @@ public abstract class StackAdvisorResourceProvider
extends ReadOnlyResourceProvi
@SuppressWarnings("unchecked")
protected StackAdvisorRequest prepareStackAdvisorRequest(Request request) {
try {
+ String clusterIdProperty = (String) getRequestProperty(request,
CLUSTER_ID_PROPERTY);
+ Long clusterId = clusterIdProperty == null ? null :
Long.valueOf(clusterIdProperty);
+
+ String serviceName = (String) getRequestProperty(request,
SERVICE_NAME_PROPERTY);
+
+ String autoCompleteProperty = (String) getRequestProperty(request,
AUTO_COMPLETE_PROPERTY);
+ Boolean autoComplete = autoCompleteProperty == null ? false :
Boolean.valueOf(autoCompleteProperty);
+
String stackName = (String) getRequestProperty(request,
STACK_NAME_PROPERTY_ID);
String stackVersion = (String) getRequestProperty(request,
STACK_VERSION_PROPERTY_ID);
StackAdvisorRequestType requestType = StackAdvisorRequestType
.fromString((String) getRequestProperty(request,
getRequestTypePropertyId()));
- /*
+ List<String> hosts;
+ List<String> services;
+ Map<String, Set<String>> hgComponentsMap;
+ Map<String, Set<String>> hgHostsMap;
+ Map<String, Set<String>> componentHostsMap;
+ Map<String, Map<String, Map<String, String>>> configurations;
+ Set<RecommendationResponse.ConfigGroup> configGroups;
+
+ // In auto complete case all required fields will be filled will cluster
current info
+ if (autoComplete) {
+ if (clusterId == null || serviceName == null) {
+ throw new Exception(
+ String.format("Incomplete request, clusterId and/or serviceName
are not valid, clusterId=%s, serviceName=%s",
+ clusterId, serviceName));
+ }
+ Cluster cluster = clusters.getCluster(clusterId);
+ List<Host> hostObjects = new ArrayList<>(cluster.getHosts());
+ Map<String, Service> serviceObjects = cluster.getServices();
+
+ hosts = hostObjects.stream().map(h ->
h.getHostName()).collect(Collectors.toList());
+ services = new ArrayList<>(serviceObjects.keySet());
+ hgComponentsMap = calculateHostGroupComponentsMap(cluster);
+ hgHostsMap = calculateHostGroupHostsMap(cluster);
+ componentHostsMap = calculateComponentHostsMap(cluster);
+ configurations = calculateConfigurations(cluster, serviceName);
+
+ configGroups = calculateConfigGroups(cluster, request);
+ } else {
+ /*
* ClassCastException will occur if hosts or services are empty in the
* request.
- *
+ *
* @see JsonRequestBodyParser for arrays parsing
*/
- Object hostsObject = getRequestProperty(request, HOST_PROPERTY);
- if (hostsObject instanceof LinkedHashSet) {
- if (((LinkedHashSet)hostsObject).isEmpty()) {
- throw new Exception("Empty host list passed to recommendation
service");
+ Object hostsObject = getRequestProperty(request, HOST_PROPERTY);
+ if (hostsObject instanceof LinkedHashSet) {
+ if (((LinkedHashSet)hostsObject).isEmpty()) {
+ throw new Exception("Empty host list passed to recommendation
service");
+ }
}
- }
- List<String> hosts = (List<String>) hostsObject;
+ hosts = (List<String>) hostsObject;
- Object servicesObject = getRequestProperty(request, SERVICES_PROPERTY);
- if (servicesObject instanceof LinkedHashSet) {
- if (((LinkedHashSet)servicesObject).isEmpty()) {
- throw new Exception("Empty service list passed to recommendation
service");
+ Object servicesObject = getRequestProperty(request, SERVICES_PROPERTY);
+ if (servicesObject instanceof LinkedHashSet) {
+ if (((LinkedHashSet)servicesObject).isEmpty()) {
+ throw new Exception("Empty service list passed to recommendation
service");
+ }
}
- }
- List<String> services = (List<String>) servicesObject;
+ services = (List<String>) servicesObject;
- Map<String, Set<String>> hgComponentsMap =
calculateHostGroupComponentsMap(request);
- Map<String, Set<String>> hgHostsMap =
calculateHostGroupHostsMap(request);
- Map<String, Set<String>> componentHostsMap =
calculateComponentHostsMap(hgComponentsMap,
- hgHostsMap);
- Map<String, Map<String, Map<String, String>>> configurations =
calculateConfigurations(request);
+ hgComponentsMap = calculateHostGroupComponentsMap(request);
+ hgHostsMap = calculateHostGroupHostsMap(request);
+ componentHostsMap = calculateComponentHostsMap(hgComponentsMap,
hgHostsMap);
+ configurations = calculateConfigurations(request);
+ configGroups = calculateConfigGroups(request);
+ }
Map<String, String> userContext = readUserContext(request);
Boolean gplLicenseAccepted = configuration.getGplLicenseAccepted();
-
List<ChangedConfigInfo> changedConfigurations =
requestType == StackAdvisorRequestType.CONFIGURATION_DEPENDENCIES ?
calculateChangedConfigurations(request) : Collections.emptyList();
- Set<RecommendationResponse.ConfigGroup> configGroups =
calculateConfigGroups(request);
+ String configsResponseProperty = (String) getRequestProperty(request,
CONFIGS_RESPONSE_PROPERTY);
+ Boolean configsResponse = configsResponseProperty == null ? false :
Boolean.valueOf(configsResponseProperty);
+
return StackAdvisorRequestBuilder.
forStack(stackName, stackVersion).ofType(requestType).forHosts(hosts).
forServices(services).forHostComponents(hgComponentsMap).
@@ -153,7 +216,10 @@ public abstract class StackAdvisorResourceProvider extends
ReadOnlyResourceProvi
withConfigGroups(configGroups).
withChangedConfigurations(changedConfigurations).
withUserContext(userContext).
- withGPLLicenseAccepted(gplLicenseAccepted).build();
+ withGPLLicenseAccepted(gplLicenseAccepted).
+ withClusterId(clusterId).
+ withServiceName(serviceName).
+ withConfigsResponse(configsResponse).build();
} catch (Exception e) {
LOG.warn("Error occurred during preparation of stack advisor request",
e);
Response response = Response.status(Status.BAD_REQUEST)
@@ -194,6 +260,28 @@ public abstract class StackAdvisorResourceProvider extends
ReadOnlyResourceProvi
}
/**
+ * Retrieves component names mapped by host groups, host name is used as
host group identifier
+ * @param cluster cluster for calculating components mapping by host groups
+ * @return map "host group name" -> ["component name1", "component name 2",
...]
+ */
+ private Map<String, Set<String>> calculateHostGroupComponentsMap(Cluster
cluster) {
+ Map<String, Set<String>> map = new HashMap<>();
+ List<Host> hosts = new ArrayList<>(cluster.getHosts());
+ if (!hosts.isEmpty()) {
+ for (Host host : hosts) {
+ String hostGroupName = host.getHostName();
+
+ Set<String> components = new HashSet<>();
+ for (ServiceComponentHost sch :
cluster.getServiceComponentHosts(host.getHostName())) {
+ components.add(sch.getServiceComponentName());
+ }
+ map.put(hostGroupName, components);
+ }
+ }
+ return map;
+ }
+
+ /**
* Will prepare host-group names to hosts names map from the recommendation
* binding host groups.
*
@@ -224,6 +312,24 @@ public abstract class StackAdvisorResourceProvider extends
ReadOnlyResourceProvi
return map;
}
+ /**
+ * Retrieves hosts names mapped by host groups, host name is used as host
group identifier
+ * @param cluster cluster for calculating hosts mapping by host groups
+ * @return map "host group name" -> ["host name 1"]
+ */
+ private Map<String, Set<String>> calculateHostGroupHostsMap(Cluster cluster)
{
+ Map<String, Set<String>> map = new HashMap<>();
+
+ List<Host> hosts = new ArrayList<>(cluster.getHosts());
+ if (!hosts.isEmpty()) {
+ for (Host host : hosts) {
+ map.put(host.getHostName(), Collections.singleton(host.getHostName()));
+ }
+ }
+
+ return map;
+ }
+
protected List<ChangedConfigInfo> calculateChangedConfigurations(Request
request) {
List<ChangedConfigInfo> configs =
new LinkedList<>();
@@ -270,6 +376,39 @@ public abstract class StackAdvisorResourceProvider extends
ReadOnlyResourceProvi
return configGroups;
}
+ protected Set<RecommendationResponse.ConfigGroup>
calculateConfigGroups(Cluster cluster, Request request) {
+
+ Set<RecommendationResponse.ConfigGroup> configGroups = new HashSet<>();
+
+ Set<HashMap<String, Object>> configGroupsProperties =
+ (HashSet<HashMap<String, Object>>) getRequestProperty(request,
CONFIG_GROUPS_PROPERTY);
+ if (configGroupsProperties != null) {
+ for (HashMap<String, Object> props : configGroupsProperties) {
+ RecommendationResponse.ConfigGroup configGroup = new
RecommendationResponse.ConfigGroup();
+ Object groupIdObject = props.get(CONFIG_GROUPS_GROUP_ID_PROPERTY);
+ if (groupIdObject != null) {
+ Long groupId = Long.valueOf((String) groupIdObject);
+ ConfigGroup clusterConfigGroup =
cluster.getConfigGroupsById(groupId);
+
+ // convert configs
+ Map<String, RecommendationResponse.BlueprintConfigurations>
typedConfiguration = new HashMap<>();
+ for (Map.Entry<String, Config> config :
clusterConfigGroup.getConfigurations().entrySet()) {
+ RecommendationResponse.BlueprintConfigurations
blueprintConfiguration = new RecommendationResponse.BlueprintConfigurations();
+
blueprintConfiguration.setProperties(config.getValue().getProperties());
+ typedConfiguration.put(config.getKey(), blueprintConfiguration);
+ }
+
+ configGroup.setConfigurations(typedConfiguration);
+
+
configGroup.setHosts(clusterConfigGroup.getHosts().values().stream().map(h ->
h.getHostName()).collect(Collectors.toList()));
+ configGroups.add(configGroup);
+ }
+ }
+ }
+
+ return configGroups;
+ }
+
/**
* Parse the user contex for the call. Typical structure
* { "operation" : "createCluster" }
@@ -278,7 +417,7 @@ public abstract class StackAdvisorResourceProvider extends
ReadOnlyResourceProvi
* @return
*/
protected Map<String, String> readUserContext(Request request) {
- HashMap<String, String> userContext = new HashMap<>();
+ Map<String, String> userContext = new HashMap<>();
if (null != getRequestProperty(request, USER_CONTEXT_OPERATION_PROPERTY)) {
userContext.put(OPERATION_PROPERTY,
(String) getRequestProperty(request,
USER_CONTEXT_OPERATION_PROPERTY));
@@ -331,9 +470,33 @@ public abstract class StackAdvisorResourceProvider extends
ReadOnlyResourceProvi
return configurations;
}
+ protected Map<String, Map<String, Map<String, String>>>
calculateConfigurations(Cluster cluster, String serviceName)
+ throws AmbariException {
+ Map<String, Map<String, Map<String, String>>> configurations = new
HashMap<>();
+ Service service = cluster.getService(serviceName);
+
+ StackId stackId = service.getDesiredStackId();
+ ServiceInfo serviceInfo = ambariMetaInfo.getService(stackId.getStackName(),
+ stackId.getStackVersion(), serviceName);
+
+ List<String> requiredConfigTypes =
serviceInfo.getConfigDependenciesWithComponents();
+ Map<String, DesiredConfig> desiredConfigs = cluster.getDesiredConfigs();
+ Map<String, DesiredConfig> requiredDesiredConfigs = new HashMap<>();
+ for (String requiredConfigType : requiredConfigTypes) {
+ if (desiredConfigs.containsKey(requiredConfigType)) {
+ requiredDesiredConfigs.put(requiredConfigType,
desiredConfigs.get(requiredConfigType));
+ }
+ }
+ for (Map.Entry<String, DesiredConfig> requiredDesiredConfigEntry :
requiredDesiredConfigs.entrySet()) {
+ Config config = cluster.getConfig(requiredDesiredConfigEntry.getKey(),
requiredDesiredConfigEntry.getValue().getTag());
+ configurations.put(requiredDesiredConfigEntry.getKey(),
Collections.singletonMap("properties", config.getProperties()));
+ }
+ return configurations;
+ }
+
@SuppressWarnings("unchecked")
private Map<String, Set<String>> calculateComponentHostsMap(Map<String,
Set<String>> hostGroups,
- Map<String, Set<String>> bindingHostGroups) {
+ Map<String,
Set<String>> bindingHostGroups) {
/*
* ClassCastException may occur in case of body inconsistency: property
* missed, etc.
@@ -360,6 +523,23 @@ public abstract class StackAdvisorResourceProvider extends
ReadOnlyResourceProvi
return componentHostsMap;
}
+ @SuppressWarnings("unchecked")
+ private Map<String, Set<String>> calculateComponentHostsMap(Cluster cluster)
{
+ /*
+ * ClassCastException may occur in case of body inconsistency: property
+ * missed, etc.
+ */
+
+ Map<String, Set<String>> componentHostsMap = new HashMap<>();
+ List<ServiceComponentHost> schs = cluster.getServiceComponentHosts();
+ for (ServiceComponentHost sch : schs) {
+ componentHostsMap.putIfAbsent(sch.getServiceComponentName(), new
HashSet<>());
+
componentHostsMap.get(sch.getServiceComponentName()).add(sch.getHostName());
+ }
+
+ return componentHostsMap;
+ }
+
protected Object getRequestProperty(Request request, String propertyName) {
for (Map<String, Object> propertyMap : request.getProperties()) {
if (propertyMap.containsKey(propertyName)) {
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/hosts/HostUpdateListener.java
b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/hosts/HostUpdateListener.java
index 4840bc0..cf43b6c 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/hosts/HostUpdateListener.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/hosts/HostUpdateListener.java
@@ -22,6 +22,7 @@ import java.util.Map;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.EagerSingleton;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
import org.apache.ambari.server.events.AlertEvent;
import org.apache.ambari.server.events.AlertStateChangeEvent;
import org.apache.ambari.server.events.HostStateUpdateEvent;
@@ -34,7 +35,6 @@ import
org.apache.ambari.server.events.publishers.AmbariEventPublisher;
import org.apache.ambari.server.events.publishers.STOMPUpdatePublisher;
import org.apache.ambari.server.orm.dao.AlertSummaryDTO;
import org.apache.ambari.server.orm.dao.AlertsDAO;
-import org.apache.ambari.server.orm.dao.ServiceDesiredStateDAO;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.Host;
@@ -56,15 +56,15 @@ public class HostUpdateListener {
private STOMPUpdatePublisher STOMPUpdatePublisher;
@Inject
- private ServiceDesiredStateDAO serviceDesiredStateDAO;
-
- @Inject
private AlertsDAO alertsDAO;
@Inject
private Provider<Clusters> m_clusters;
@Inject
+ private Provider<StackAdvisorHelper> stackAdvisorHelperProvider;
+
+ @Inject
public HostUpdateListener(AmbariEventPublisher ambariEventPublisher,
AlertEventPublisher m_alertEventPublisher) {
ambariEventPublisher.register(this);
m_alertEventPublisher.register(this);
@@ -92,6 +92,7 @@ public class HostUpdateListener {
hostUpdateEvent.getHostName(),
hostUpdateEvent.getHostStatus(),
hostUpdateEvent.getLastHeartbeatTime()));
+ stackAdvisorHelperProvider.get().clearCaches(hostName);
}
}
@@ -118,6 +119,7 @@ public class HostUpdateListener {
hostUpdateEvent.getHostState(),
hostUpdateEvent.getLastHeartbeatTime()));
}
+ stackAdvisorHelperProvider.get().clearCaches(hostName);
}
@Subscribe
@@ -175,6 +177,7 @@ public class HostUpdateListener {
STOMPUpdatePublisher.publish(HostUpdateEvent.createHostAlertsUpdate(hostUpdateEvent.getClusterName(),
hostName, summary));
}
+ stackAdvisorHelperProvider.get().clearCaches(hostName);
} else if (event.getService()!= null) {
String serviceName = event.getService().getName();
for (String hostName :
m_clusters.get().getCluster(clusterId).getService(serviceName).getServiceHosts())
{
@@ -189,6 +192,7 @@ public class HostUpdateListener {
STOMPUpdatePublisher.publish(HostUpdateEvent.createHostAlertsUpdate(hostUpdateEvent.getClusterName(),
hostName, summary));
+ stackAdvisorHelperProvider.get().clearCaches(hostName);
}
}
}
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index 0955200..dc19e06 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -511,6 +511,13 @@ public interface Cluster {
throws AmbariException;
/**
+ * Find config group by config group id
+ * @param configId id of config group to return
+ * @return config group
+ */
+ ConfigGroup getConfigGroupsById(Long configId);
+
+ /**
* Add a @RequestExecution to the cluster
* @param requestExecution
* @throws AmbariException
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java
b/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java
index e2b5911..cdf36f0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Host.java
@@ -28,6 +28,7 @@ import org.apache.ambari.server.agent.HostInfo;
import org.apache.ambari.server.agent.RecoveryReport;
import org.apache.ambari.server.controller.HostResponse;
import org.apache.ambari.server.orm.entities.HostEntity;
+import org.apache.ambari.server.orm.entities.HostStateEntity;
import org.apache.ambari.server.orm.entities.HostVersionEntity;
import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
@@ -176,6 +177,13 @@ public interface Host extends Comparable {
*/
String getOsFamily();
+ /**
+ * Gets the os family from host attributes
+ * @param hostAttributes host attributes
+ * @return the os family for host
+ */
+ String getOsFamily(Map<String, String> hostAttributes);
+
String getOSFamilyFromHostAttributes(Map<String, String> hostAttributes);
/**
@@ -200,6 +208,13 @@ public interface Host extends Comparable {
HostHealthStatus getHealthStatus();
/**
+ * Gets the health status from host attributes
+ * @param hostStateEntity host attributes
+ * @return the health status
+ */
+ HostHealthStatus getHealthStatus(HostStateEntity hostStateEntity);
+
+ /**
* Get detailed recovery report for the host
* @return
*/
@@ -223,6 +238,13 @@ public interface Host extends Comparable {
Map<String, String> getHostAttributes();
/**
+ * Gets host attributes from host entity
+ * @param hostEntity host entity
+ * @return the host attributes
+ */
+ Map<String, String> getHostAttributes(HostEntity hostEntity);
+
+ /**
* @param hostAttributes the hostAttributes to set
*/
void setHostAttributes(Map<String, String> hostAttributes);
@@ -290,6 +312,13 @@ public interface Host extends Comparable {
AgentVersion getAgentVersion();
/**
+ * Gets version of the ambari agent running on the host.
+ * @param hostStateEntity host state entity
+ * @return the agentVersion
+ */
+ AgentVersion getAgentVersion(HostStateEntity hostStateEntity);
+
+ /**
* @param agentVersion the agentVersion to set
*/
void setAgentVersion(AgentVersion agentVersion);
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index 6dbabc4..22b5a9f 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -33,6 +33,8 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
@@ -531,6 +533,11 @@ public class ClusterImpl implements Cluster {
}
@Override
+ public ConfigGroup getConfigGroupsById(Long configId) {
+ return clusterConfigGroups.get(configId);
+ }
+
+ @Override
public void addRequestExecution(RequestExecution requestExecution) throws
AmbariException {
LOG.info("Adding a new request schedule" + ", clusterName = " +
getClusterName() + ", id = "
+ requestExecution.getId() + ", description = " +
requestExecution.getDescription());
@@ -800,7 +807,7 @@ public class ClusterImpl implements Cluster {
@Override
public Map<String, Set<String>> getServiceComponentHostMap(Set<String>
hostNames, Set<String> serviceNames) {
- Map<String, Set<String>> componentHostMap = new HashMap<>();
+ Map<String, Set<String>> componentHostMap = new TreeMap<>();
Collection<Host> hosts = getHosts();
@@ -820,7 +827,7 @@ public class ClusterImpl implements Cluster {
Set<String> componentHosts = componentHostMap.get(component);
if (componentHosts == null) {
- componentHosts = new HashSet<>();
+ componentHosts = new TreeSet<>();
componentHostMap.put(component, componentHosts);
}
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
b/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
index 0401a1c..3a867ee 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/state/host/HostImpl.java
@@ -765,6 +765,11 @@ public class HostImpl implements Host {
}
@Override
+ public String getOsFamily(Map<String, String> hostAttributes) {
+ return getOSFamilyFromHostAttributes(hostAttributes);
+ }
+
+ @Override
public String getOSFamilyFromHostAttributes(Map<String, String>
hostAttributes) {
try {
String majorVersion =
hostAttributes.get(OS_RELEASE_VERSION).split("\\.")[0];
@@ -806,6 +811,15 @@ public class HostImpl implements Host {
}
@Override
+ public HostHealthStatus getHealthStatus(HostStateEntity hostStateEntity) {
+ if (hostStateEntity != null) {
+ return gson.fromJson(hostStateEntity.getHealthStatus(),
HostHealthStatus.class);
+ }
+
+ return null;
+ }
+
+ @Override
public void setHealthStatus(HostHealthStatus healthStatus) {
HostStateEntity hostStateEntity = getHostStateEntity();
if (hostStateEntity != null) {
@@ -837,6 +851,11 @@ public class HostImpl implements Host {
}
@Override
+ public Map<String, String> getHostAttributes(HostEntity hostEntity) {
+ return gson.fromJson(hostEntity.getHostAttributes(), hostAttributesType);
+ }
+
+ @Override
public void setHostAttributes(Map<String, String> hostAttributes) {
HostEntity hostEntity = getHostEntity();
Map<String, String> hostAttrs =
gson.fromJson(hostEntity.getHostAttributes(), hostAttributesType);
@@ -903,6 +922,15 @@ public class HostImpl implements Host {
}
@Override
+ public AgentVersion getAgentVersion(HostStateEntity hostStateEntity) {
+ if (hostStateEntity != null) {
+ return gson.fromJson(hostStateEntity.getAgentVersion(),
AgentVersion.class);
+ }
+
+ return null;
+ }
+
+ @Override
public void setAgentVersion(AgentVersion agentVersion) {
HostStateEntity hostStateEntity = getHostStateEntity();
if (hostStateEntity != null) {
@@ -963,26 +991,33 @@ public class HostImpl implements Host {
public HostResponse convertToResponse() {
HostResponse r = new HostResponse(getHostName());
- r.setAgentVersion(getAgentVersion());
- r.setPhCpuCount(getPhCpuCount());
- r.setCpuCount(getCpuCount());
+ HostEntity hostEntity = getHostEntity();
+ HostStateEntity hostStateEntity = getHostStateEntity();
+
+ Map<String, String> hostAttributes = getHostAttributes(hostEntity);
+ r.setHostAttributes(hostAttributes);
+ r.setOsFamily(getOsFamily(hostAttributes));
+
+ r.setAgentVersion(getAgentVersion(hostStateEntity));
+ r.setHealthStatus(getHealthStatus(hostStateEntity));
+
+ r.setPhCpuCount(hostEntity.getPhCpuCount());
+ r.setCpuCount(hostEntity.getCpuCount());
+ r.setIpv4(hostEntity.getIpv4());
+ r.setOsArch(hostEntity.getOsArch());
+ r.setOsType(hostEntity.getOsType());
+ r.setTotalMemBytes(hostEntity.getTotalMem());
+ r.setLastRegistrationTime(hostEntity.getLastRegistrationTime());
+ r.setPublicHostName(hostEntity.getPublicHostName());
+ r.setRackInfo(hostEntity.getRackInfo());
+
r.setDisksInfo(getDisksInfo());
- r.setHealthStatus(getHealthStatus());
- r.setHostAttributes(getHostAttributes());
- r.setIpv4(getIPv4());
+ r.setStatus(getStatus());
r.setLastHeartbeatTime(getLastHeartbeatTime());
r.setLastAgentEnv(lastAgentEnv);
- r.setLastRegistrationTime(getLastRegistrationTime());
- r.setOsArch(getOsArch());
- r.setOsType(getOsType());
- r.setOsFamily(getOsFamily());
- r.setRackInfo(getRackInfo());
- r.setTotalMemBytes(getTotalMemBytes());
- r.setPublicHostName(getPublicHostName());
- r.setHostState(getState());
- r.setStatus(getStatus());
r.setRecoveryReport(getRecoveryReport());
r.setRecoverySummary(getRecoveryReport().getSummary());
+ r.setHostState(getState());
return r;
}
diff --git
a/ambari-server/src/main/java/org/apache/ambari/server/topology/STOMPComponentsDeleteHandler.java
b/ambari-server/src/main/java/org/apache/ambari/server/topology/STOMPComponentsDeleteHandler.java
index c48d4cf..79dd948 100644
---
a/ambari-server/src/main/java/org/apache/ambari/server/topology/STOMPComponentsDeleteHandler.java
+++
b/ambari-server/src/main/java/org/apache/ambari/server/topology/STOMPComponentsDeleteHandler.java
@@ -120,8 +120,8 @@ public class STOMPComponentsDeleteHandler {
.setComponentName(hostComponent.getComponentName())
.setServiceName(hostComponent.getServiceName())
.setVersion(hostComponent.getVersion())
- .setHostIds(new HashSet<>(Arrays.asList(hostComponent.getHostId())))
- .setHostNames(new
HashSet<>(Arrays.asList(hostComponent.getHostName())))
+ .setHostIdentifiers(new
HashSet<>(Arrays.asList(hostComponent.getHostId())),
+ new HashSet<>(Arrays.asList(hostComponent.getHostName())))
.setLastComponentState(hostComponent.getLastComponentState())
.build();
diff --git
a/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/TopologyClusterTest.java
b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/TopologyClusterTest.java
new file mode 100644
index 0000000..680523a
--- /dev/null
+++
b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/TopologyClusterTest.java
@@ -0,0 +1,186 @@
+/**
+ * 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.agent.stomp;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+import org.apache.ambari.server.NullHostNameException;
+import org.apache.ambari.server.agent.stomp.dto.TopologyCluster;
+import org.apache.ambari.server.agent.stomp.dto.TopologyComponent;
+import org.apache.ambari.server.agent.stomp.dto.TopologyHost;
+import org.apache.ambari.server.agent.stomp.dto.TopologyUpdateHandlingReport;
+import org.apache.ambari.server.events.UpdateEventType;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TopologyClusterTest {
+
+ @Test
+ public void testHandlingReportHostAdd() throws NullHostNameException {
+ TopologyHost dummyHost = new TopologyHost(1L, "hostName1");
+ TopologyHost hostToAddition = new TopologyHost(2L, "hostName2");
+
+ TopologyCluster topologyCluster = new TopologyCluster(new HashSet<>(), new
HashSet(){{add(dummyHost);}});
+
+ TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+ topologyCluster.update(Collections.emptySet(),
Collections.singleton(hostToAddition), UpdateEventType.UPDATE, report);
+
+ Assert.assertEquals(1L, report.getUpdatedHostNames().size());
+ Assert.assertEquals("hostName2",
report.getUpdatedHostNames().iterator().next());
+
+ Assert.assertEquals(2L, topologyCluster.getTopologyHosts().size());
+ }
+
+ @Test
+ public void testHandlingReportHostDelete() throws NullHostNameException {
+ TopologyHost dummyHost = new TopologyHost(1L, "hostName1");
+ TopologyHost hostToDelete = new TopologyHost(2L, "hostName2");
+ TopologyHost update = new TopologyHost(2L, "hostName2");
+
+ TopologyCluster topologyCluster = new TopologyCluster(new HashSet<>(), new
HashSet(){{add(dummyHost); add(hostToDelete);}});
+
+ TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+ topologyCluster.update(Collections.emptySet(),
Collections.singleton(update), UpdateEventType.DELETE, report);
+
+ Assert.assertEquals(1L, report.getUpdatedHostNames().size());
+ Assert.assertEquals("hostName2",
report.getUpdatedHostNames().iterator().next());
+
+ Assert.assertEquals(1L, topologyCluster.getTopologyHosts().size());
+ Assert.assertEquals("hostName1",
topologyCluster.getTopologyHosts().iterator().next().getHostName());
+ }
+
+ @Test
+ public void testHandlingReportHostUpdate() throws NullHostNameException {
+ TopologyHost dummyHost = new TopologyHost(1L, "hostName1");
+ TopologyHost hostToUpdate = new TopologyHost(2L, "hostName2");
+ TopologyHost update = new TopologyHost(2L, "hostName2", "rack","ipv4");
+
+ TopologyCluster topologyCluster = new TopologyCluster(new HashSet<>(), new
HashSet(){{add(dummyHost); add(hostToUpdate);}});
+
+ TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+ topologyCluster.update(Collections.emptySet(),
Collections.singleton(update), UpdateEventType.UPDATE, report);
+
+ Assert.assertEquals(1L, report.getUpdatedHostNames().size());
+ Assert.assertEquals("hostName2",
report.getUpdatedHostNames().iterator().next());
+
+ Assert.assertEquals(2L, topologyCluster.getTopologyHosts().size());
+ }
+
+ @Test
+ public void testHandlingReportComponentAdd() throws NullHostNameException {
+ TopologyComponent dummyComponent = createDummyTopologyComponent("comp1",
+ new Long[]{1L, 2L}, new String[]{"hostName1", "hostName2"});
+ TopologyComponent componentToAddition =
createDummyTopologyComponent("comp2",
+ new Long[]{1L, 3L}, new String[]{"hostName1", "hostName3"});
+
+ TopologyCluster topologyCluster = new TopologyCluster(new
HashSet(){{add(dummyComponent);}}, new HashSet<>());
+
+ TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+ topologyCluster.update(Collections.singleton(componentToAddition),
Collections.emptySet(), UpdateEventType.UPDATE, report);
+
+ Assert.assertEquals(2L, report.getUpdatedHostNames().size());
+ Assert.assertTrue(report.getUpdatedHostNames().contains("hostName1"));
+ Assert.assertTrue(report.getUpdatedHostNames().contains("hostName3"));
+
+ Assert.assertEquals(2L, topologyCluster.getTopologyComponents().size());
+ }
+
+ @Test
+ public void testHandlingReportComponentDeletePartially() throws
NullHostNameException {
+ TopologyComponent dummyComponent = createDummyTopologyComponent("comp1",
+ new Long[]{1L, 2L}, new String[]{"hostName1", "hostName2"});
+ TopologyComponent componentToDelete = createDummyTopologyComponent("comp2",
+ new Long[]{1L, 3L}, new String[]{"hostName1", "hostName3"});
+
+ TopologyComponent update = createDummyTopologyComponent("comp2",
+ new Long[]{1L}, new String[]{"hostName1"});
+
+ TopologyCluster topologyCluster = new TopologyCluster(
+ new HashSet(){{add(dummyComponent); add(componentToDelete);}}, new
HashSet<>());
+
+ TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+ topologyCluster.update(Collections.singleton(update),
Collections.emptySet(), UpdateEventType.DELETE, report);
+
+ Assert.assertEquals(1L, report.getUpdatedHostNames().size());
+ Assert.assertTrue(report.getUpdatedHostNames().contains("hostName1"));
+
+ Assert.assertEquals(2L, topologyCluster.getTopologyComponents().size());
+ }
+
+ @Test
+ public void testHandlingReportComponentDeleteFully() throws
NullHostNameException {
+ TopologyComponent dummyComponent = createDummyTopologyComponent("comp1",
+ new Long[]{1L, 2L}, new String[]{"hostName1", "hostName2"});
+ TopologyComponent componentToDelete = createDummyTopologyComponent("comp2",
+ new Long[]{1L, 3L}, new String[]{"hostName1", "hostName3"});
+
+ TopologyComponent update = createDummyTopologyComponent("comp2",
+ new Long[]{1L, 3L}, new String[]{"hostName1", "hostName3"});
+
+ TopologyCluster topologyCluster = new TopologyCluster(
+ new HashSet(){{add(dummyComponent); add(componentToDelete);}}, new
HashSet<>());
+
+ TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+ topologyCluster.update(Collections.singleton(update),
Collections.emptySet(), UpdateEventType.DELETE, report);
+
+ Assert.assertEquals(2L, report.getUpdatedHostNames().size());
+ Assert.assertTrue(report.getUpdatedHostNames().contains("hostName1"));
+ Assert.assertTrue(report.getUpdatedHostNames().contains("hostName3"));
+
+ Assert.assertEquals(1L, topologyCluster.getTopologyComponents().size());
+ }
+
+ @Test
+ public void testHandlingReportComponentUpdate() throws NullHostNameException
{
+ TopologyComponent dummyComponent = createDummyTopologyComponent("comp1",
+ new Long[]{1L, 2L}, new String[]{"hostName1", "hostName2"});
+ TopologyComponent componentToUpdate = createDummyTopologyComponent("comp2",
+ new Long[]{1L, 3L}, new String[]{"hostName1", "hostName3"});
+
+ TopologyComponent update = createDummyTopologyComponent("comp2",
+ new Long[]{1L, 4L}, new String[]{"hostName1", "hostName4"});
+
+ TopologyCluster topologyCluster = new TopologyCluster(
+ new HashSet(){{add(dummyComponent); add(componentToUpdate);}}, new
HashSet<>());
+
+ TopologyUpdateHandlingReport report = new TopologyUpdateHandlingReport();
+
+ topologyCluster.update(Collections.singleton(update),
Collections.emptySet(), UpdateEventType.UPDATE, report);
+
+ Assert.assertEquals(1L, report.getUpdatedHostNames().size());
+ Assert.assertTrue(report.getUpdatedHostNames().contains("hostName4"));
+
+ Assert.assertEquals(2L, topologyCluster.getTopologyComponents().size());
+ }
+
+ private TopologyComponent createDummyTopologyComponent(String componentName,
Long[] hostIds, String[] hostNames) {
+ return TopologyComponent.newBuilder()
+ .setComponentName(componentName)
+ .setServiceName("serviceName")
+ .setHostIdentifiers(new HashSet<Long>(Arrays.asList(hostIds)), new
HashSet<String>(Arrays.asList(hostNames)))
+ .build();
+ }
+}
diff --git
a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessorTest.java
b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessorTest.java
index bc82999..abc4d22 100644
---
a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessorTest.java
+++
b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorBlueprintProcessorTest.java
@@ -32,6 +32,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import org.apache.ambari.server.AmbariException;
import
org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
import
org.apache.ambari.server.controller.internal.ConfigurationTopologyException;
import org.apache.ambari.server.controller.internal.Stack;
@@ -71,7 +72,7 @@ public class StackAdvisorBlueprintProcessorTest {
}
@Test
- public void testAdviseConfiguration() throws StackAdvisorException,
ConfigurationTopologyException {
+ public void testAdviseConfiguration() throws StackAdvisorException,
ConfigurationTopologyException, AmbariException {
// GIVEN
Map<String, Map<String, String>> props = createProps();
Map<String, AdvisedConfiguration> advisedConfigurations = new HashMap<>();
@@ -106,7 +107,7 @@ public class StackAdvisorBlueprintProcessorTest {
}
@Test
- public void testAdviseConfigurationWithOnlyStackDefaultsApply() throws
StackAdvisorException, ConfigurationTopologyException {
+ public void testAdviseConfigurationWithOnlyStackDefaultsApply() throws
StackAdvisorException, ConfigurationTopologyException, AmbariException {
// GIVEN
Map<String, Map<String, String>> props = createProps();
Map<String, AdvisedConfiguration> advisedConfigurations = new HashMap<>();
@@ -141,7 +142,7 @@ public class StackAdvisorBlueprintProcessorTest {
}
@Test
- public void
testAdviseConfigurationWithOnlyStackDefaultsApplyWhenNoUserInputForDefault()
throws StackAdvisorException, ConfigurationTopologyException {
+ public void
testAdviseConfigurationWithOnlyStackDefaultsApplyWhenNoUserInputForDefault()
throws StackAdvisorException, ConfigurationTopologyException, AmbariException {
// GIVEN
Map<String, Map<String, String>> props = createProps();
props.get("core-site").put("dummyKey3", "stackDefaultValue");
@@ -176,7 +177,7 @@ public class StackAdvisorBlueprintProcessorTest {
@Test
public void
testAdviseConfigurationWith_ALWAYS_APPLY_DONT_OVERRIDE_CUSTOM_VALUES() throws
StackAdvisorException,
- ConfigurationTopologyException {
+ ConfigurationTopologyException, AmbariException {
// GIVEN
Map<String, Map<String, String>> props = createProps();
Map<String, AdvisedConfiguration> advisedConfigurations = new HashMap<>();
@@ -209,7 +210,7 @@ public class StackAdvisorBlueprintProcessorTest {
}
@Test
- public void testAdviseConfigurationWhenConfigurationRecommendFails() throws
StackAdvisorException, ConfigurationTopologyException {
+ public void testAdviseConfigurationWhenConfigurationRecommendFails() throws
StackAdvisorException, ConfigurationTopologyException, AmbariException {
// GIVEN
Map<String, Map<String, String>> props = createProps();
Map<String, AdvisedConfiguration> advisedConfigurations = new HashMap<>();
@@ -238,7 +239,7 @@ public class StackAdvisorBlueprintProcessorTest {
}
@Test
- public void
testAdviseConfigurationWhenConfigurationRecommendHasInvalidResponse() throws
StackAdvisorException, ConfigurationTopologyException {
+ public void
testAdviseConfigurationWhenConfigurationRecommendHasInvalidResponse() throws
StackAdvisorException, ConfigurationTopologyException, AmbariException {
// GIVEN
Map<String, Map<String, String>> props = createProps();
Map<String, AdvisedConfiguration> advisedConfigurations = new HashMap<>();
diff --git
a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java
b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java
index 2b3e6bb..3bbb9f7 100644
---
a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java
+++
b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java
@@ -18,6 +18,15 @@
package org.apache.ambari.server.api.services.stackadvisor;
+import static org.easymock.EasyMock.anyString;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.partialMockBuilder;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -27,6 +36,11 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import
org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestBuilder;
@@ -41,10 +55,15 @@ import
org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorC
import
org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
import
org.apache.ambari.server.api.services.stackadvisor.validations.ValidationResponse;
import org.apache.ambari.server.configuration.Configuration;
+import
org.apache.ambari.server.controller.internal.AmbariServerConfigurationHandler;
import org.apache.ambari.server.state.ServiceInfo;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.node.JsonNodeFactory;
import org.junit.Test;
import org.mockito.Mockito;
+import com.google.gson.Gson;
+
/**
* StackAdvisorHelper unit tests.
*/
@@ -160,7 +179,7 @@ public class StackAdvisorHelperTest {
ServiceInfo service = mock(ServiceInfo.class);
when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(),
Mockito.anyString())).thenReturn(service);
when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
- StackAdvisorHelper helper = new StackAdvisorHelper(configuration,
saRunner, metaInfo, null);
+ StackAdvisorHelper helper = new StackAdvisorHelper(configuration,
saRunner, metaInfo, null, null);
StackAdvisorRequestType requestType = StackAdvisorRequestType.HOST_GROUPS;
StackAdvisorRequest request =
StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.ofType(requestType).build();
@@ -196,7 +215,7 @@ public class StackAdvisorHelperTest {
ServiceInfo service = mock(ServiceInfo.class);
when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(),
Mockito.anyString())).thenReturn(service);
when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
- StackAdvisorHelper helper = new StackAdvisorHelper(configuration,
saRunner, metaInfo, null);
+ StackAdvisorHelper helper = new StackAdvisorHelper(configuration,
saRunner, metaInfo, null, null);
StackAdvisorRequestType requestType = StackAdvisorRequestType.HOST_GROUPS;
StackAdvisorRequest request =
StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.ofType(requestType).build();
@@ -216,7 +235,7 @@ public class StackAdvisorHelperTest {
ServiceInfo service = mock(ServiceInfo.class);
when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(),
Mockito.anyString())).thenReturn(service);
when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
- StackAdvisorHelper helper = new StackAdvisorHelper(configuration,
saRunner, metaInfo, null);
+ StackAdvisorHelper helper = new StackAdvisorHelper(configuration,
saRunner, metaInfo, null, null);
StackAdvisorRequestType requestType =
StackAdvisorRequestType.CONFIGURATIONS;
StackAdvisorRequest request =
StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.ofType(requestType).build();
@@ -236,7 +255,7 @@ public class StackAdvisorHelperTest {
ServiceInfo service = mock(ServiceInfo.class);
when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(),
Mockito.anyString())).thenReturn(service);
when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
- StackAdvisorHelper helper = new StackAdvisorHelper(configuration,
saRunner, metaInfo, null);
+ StackAdvisorHelper helper = new StackAdvisorHelper(configuration,
saRunner, metaInfo, null, null);
StackAdvisorRequestType requestType =
StackAdvisorRequestType.CONFIGURATION_DEPENDENCIES;
StackAdvisorRequest request =
StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.ofType(requestType).build();
@@ -246,6 +265,124 @@ public class StackAdvisorHelperTest {
assertEquals(ConfigurationDependenciesRecommendationCommand.class,
command.getClass());
}
+ @Test
+ public void testClearCacheAndHost() throws IOException,
NoSuchFieldException, IllegalAccessException {
+ Field hostInfoCacheField =
StackAdvisorHelper.class.getDeclaredField("hostInfoCache");
+ Field configsRecommendationResponseField =
StackAdvisorHelper.class.getDeclaredField("configsRecommendationResponse");
+ StackAdvisorHelper helper = testClearCachesSetup(hostInfoCacheField,
configsRecommendationResponseField);
+
+ helper.clearCaches("hostName1");
+
+ Map<String, JsonNode> hostInfoCache = (Map<String, JsonNode>)
hostInfoCacheField.get(helper);
+ Map<String, RecommendationResponse> configsRecommendationResponse =
+ (Map<String, RecommendationResponse>)
configsRecommendationResponseField.get(helper);
+
+ assertEquals(2, hostInfoCache.size());
+ assertTrue(hostInfoCache.containsKey("hostName2"));
+ assertTrue(hostInfoCache.containsKey("hostName3"));
+ assertTrue(configsRecommendationResponse.isEmpty());
+ }
+
+ @Test
+ public void testClearCacheAndHosts() throws IOException,
NoSuchFieldException, IllegalAccessException {
+
+ Field hostInfoCacheField =
StackAdvisorHelper.class.getDeclaredField("hostInfoCache");
+ Field configsRecommendationResponseField =
StackAdvisorHelper.class.getDeclaredField("configsRecommendationResponse");
+ StackAdvisorHelper helper = testClearCachesSetup(hostInfoCacheField,
configsRecommendationResponseField);
+
+ helper.clearCaches(new HashSet<>(Arrays.asList(new String[]{"hostName1",
"hostName2"})));
+
+ Map<String, JsonNode> hostInfoCache = (Map<String, JsonNode>)
hostInfoCacheField.get(helper);
+ Map<String, RecommendationResponse> configsRecommendationResponse =
+ (Map<String, RecommendationResponse>)
configsRecommendationResponseField.get(helper);
+
+ assertEquals(1, hostInfoCache.size());
+ assertTrue(hostInfoCache.containsKey("hostName3"));
+ assertTrue(configsRecommendationResponse.isEmpty());
+ }
+
+ @Test
+ public void testCacheRecommendations() throws IOException,
StackAdvisorException {
+ Configuration configuration = createNiceMock(Configuration.class);
+ StackAdvisorRunner stackAdvisorRunner =
createNiceMock(StackAdvisorRunner.class);
+ AmbariMetaInfo ambariMetaInfo = createNiceMock(AmbariMetaInfo.class);
+ AmbariServerConfigurationHandler ambariServerConfigurationHandler =
createNiceMock(AmbariServerConfigurationHandler.class);
+
+
expect(configuration.getRecommendationsArtifactsRolloverMax()).andReturn(1);
+
+ replay(configuration, stackAdvisorRunner, ambariMetaInfo,
ambariServerConfigurationHandler);
+
+ StackAdvisorHelper helper =
partialMockBuilder(StackAdvisorHelper.class).withConstructor(Configuration.class,
+ StackAdvisorRunner.class, AmbariMetaInfo.class,
AmbariServerConfigurationHandler.class, Gson.class).
+ withArgs(configuration, stackAdvisorRunner, ambariMetaInfo,
ambariServerConfigurationHandler, new Gson()).
+ addMockedMethod("createRecommendationCommand").
+ createMock();
+
+ verify(configuration, stackAdvisorRunner, ambariMetaInfo,
ambariServerConfigurationHandler);
+ reset(ambariMetaInfo);
+
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.setServiceAdvisorType(ServiceInfo.ServiceAdvisorType.PYTHON);
+ expect(ambariMetaInfo.getService(anyString(), anyString(),
anyString())).andReturn(serviceInfo).atLeastOnce();
+
+ ConfigurationRecommendationCommand command =
createMock(ConfigurationRecommendationCommand.class);
+
+ StackAdvisorRequest request = StackAdvisorRequestBuilder.
+ forStack(null, null).ofType(StackAdvisorRequestType.CONFIGURATIONS).
+ build();
+
+ expect(helper.createRecommendationCommand(eq("ZOOKEEPER"),
eq(request))).andReturn(command).times(2);
+
+ // populate response with dummy info to check equivalence
+ RecommendationResponse response = new RecommendationResponse();
+ response.setServices(new HashSet<String>(){{add("service1");
add("service2"); add("service3");}});
+
+ // invoke() should be fired for first recommend() execution only
+ expect(command.invoke(eq(request),
eq(ServiceInfo.ServiceAdvisorType.PYTHON))).andReturn(response).once();
+
+ replay(ambariMetaInfo, helper, command);
+
+ RecommendationResponse calculatedResponse = helper.recommend(request);
+ RecommendationResponse cachedResponse = helper.recommend(request);
+
+ verify(ambariMetaInfo, helper, command);
+
+ assertEquals(response.getServices(), calculatedResponse.getServices());
+ assertEquals(response.getServices(), cachedResponse.getServices());
+ }
+
+ private StackAdvisorHelper testClearCachesSetup(Field hostInfoCacheField,
+ Field
configsRecommendationResponseField) throws IOException,
+ NoSuchFieldException, IllegalAccessException {
+ Configuration configuration = createNiceMock(Configuration.class);
+ StackAdvisorRunner stackAdvisorRunner =
createNiceMock(StackAdvisorRunner.class);
+ AmbariMetaInfo ambariMetaInfo = createNiceMock(AmbariMetaInfo.class);
+ AmbariServerConfigurationHandler ambariServerConfigurationHandler =
createNiceMock(AmbariServerConfigurationHandler.class);
+
+ replay(configuration, stackAdvisorRunner, ambariMetaInfo,
ambariServerConfigurationHandler);
+
+ StackAdvisorHelper helper = new StackAdvisorHelper(configuration,
stackAdvisorRunner,
+ ambariMetaInfo, ambariServerConfigurationHandler, null);
+
+ verify(configuration, stackAdvisorRunner, ambariMetaInfo,
ambariServerConfigurationHandler);
+
+ hostInfoCacheField.setAccessible(true);
+ configsRecommendationResponseField.setAccessible(true);
+
+ Map<String, JsonNode> hostInfoCache = new ConcurrentHashMap<>();
+ JsonNodeFactory jnf = JsonNodeFactory.instance;
+ hostInfoCache.put("hostName1", jnf.nullNode());
+ hostInfoCache.put("hostName2", jnf.nullNode());
+ hostInfoCache.put("hostName3", jnf.nullNode());
+ Map<String, RecommendationResponse> configsRecommendationResponse = new
ConcurrentHashMap<>();
+ configsRecommendationResponse.put("111", new RecommendationResponse());
+ configsRecommendationResponse.put("222", new RecommendationResponse());
+
+ hostInfoCacheField.set(helper, hostInfoCache);
+ configsRecommendationResponseField.set(helper,
configsRecommendationResponse);
+ return helper;
+ }
+
private void
testCreateConfigurationRecommendationCommand(StackAdvisorRequestType
requestType, StackAdvisorCommandType expectedCommandType)
throws IOException, StackAdvisorException {
Configuration configuration = mock(Configuration.class);
@@ -255,7 +392,7 @@ public class StackAdvisorHelperTest {
ServiceInfo service = mock(ServiceInfo.class);
when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(),
Mockito.anyString())).thenReturn(service);
when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
- StackAdvisorHelper helper = new StackAdvisorHelper(configuration,
saRunner, metaInfo, null);
+ StackAdvisorHelper helper = new StackAdvisorHelper(configuration,
saRunner, metaInfo, null, null);
StackAdvisorRequest request =
StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.ofType(requestType).build();
@@ -268,6 +405,6 @@ public class StackAdvisorHelperTest {
}
private static StackAdvisorHelper stackAdvisorHelperSpy(Configuration
configuration, StackAdvisorRunner saRunner, AmbariMetaInfo metaInfo) throws
IOException {
- return spy(new StackAdvisorHelper(configuration, saRunner, metaInfo,
null));
+ return spy(new StackAdvisorHelper(configuration, saRunner, metaInfo, null,
null));
}
}
diff --git
a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommandTest.java
b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommandTest.java
index c983af2..26ea3a5 100644
---
a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommandTest.java
+++
b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommandTest.java
@@ -44,7 +44,10 @@ public class ConfigurationRecommendationCommandTest {
StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
File file = mock(File.class);
AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
- ConfigurationRecommendationCommand command = new
ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS,
file, "1w", ServiceInfo.ServiceAdvisorType.PYTHON, 1, saRunner, metaInfo,
null);
+ ConfigurationRecommendationCommand command =
+ new
ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS,
file,
+ "1w", ServiceInfo.ServiceAdvisorType.PYTHON, 1, saRunner, metaInfo,
+ null, null);
StackAdvisorRequest request = mock(StackAdvisorRequest.class);
Map<String, Set<String>> componentHostGroupMap = new HashMap<>();
diff --git
a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java
b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java
index 37b5a2c..c86c487 100644
---
a/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java
+++
b/ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandTest.java
@@ -44,6 +44,7 @@ import java.util.Map;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.apache.ambari.server.api.resources.ResourceInstance;
@@ -79,6 +80,9 @@ import com.google.common.collect.Lists;
*/
@RunWith(MockitoJUnitRunner.class)
public class StackAdvisorCommandTest {
+ private static final String SINGLE_HOST_RESPONSE =
"{\"href\":\"/api/v1/hosts?fields=Hosts/*&Hosts/host_name.in(%1$s)\",\"items\":[{\"href\":\"/api/v1/hosts/%1$s\",\"Hosts\":{\"host_name\":\"%1$s\"}}]}";
+ private static final String TWO_HOST_RESPONSE =
"{\"href\":\"/api/v1/hosts?fields=Hosts/*&Hosts/host_name.in(%1$s,%2$s)\",\"items\":[{\"href\":\"/api/v1/hosts/%1$s\",\"Hosts\":{\"host_name\":\"%1$s\"}},{\"href\":\"/api/v1/hosts/%2$s\",\"Hosts\":{\"host_name\":\"%2$s\"}}]}";
+
private TemporaryFolder temp = new TemporaryFolder();
@Mock
AmbariServerConfigurationHandler ambariServerConfigurationHandler;
@@ -102,7 +106,7 @@ public class StackAdvisorCommandTest {
AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(),
anyString());
StackAdvisorCommand<TestResource> command = spy(new
TestStackAdvisorCommand(recommendationsDir, recommendationsArtifactsLifetime,
- ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo));
+ ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo,
null));
StackAdvisorRequest request =
StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.build();
@@ -122,7 +126,7 @@ public class StackAdvisorCommandTest {
AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(),
anyString());
StackAdvisorCommand<TestResource> command = spy(new
TestStackAdvisorCommand(recommendationsDir, recommendationsArtifactsLifetime,
- ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo));
+ ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo,
null));
StackAdvisorRequest request =
StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.build();
@@ -150,7 +154,7 @@ public class StackAdvisorCommandTest {
AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(),
anyString());
StackAdvisorCommand<TestResource> command = spy(new
TestStackAdvisorCommand(recommendationsDir, recommendationsArtifactsLifetime,
- ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo));
+ ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo,
null));
StackAdvisorRequest request =
StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.build();
@@ -179,7 +183,8 @@ public class StackAdvisorCommandTest {
AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
doReturn(Collections.emptyList()).when(metaInfo).getStackParentVersions(anyString(),
anyString());
final StackAdvisorCommand<TestResource> command = spy(new
TestStackAdvisorCommand(
- recommendationsDir, recommendationsArtifactsLifetime,
ServiceInfo.ServiceAdvisorType.PYTHON, requestId, saRunner, metaInfo));
+ recommendationsDir, recommendationsArtifactsLifetime,
ServiceInfo.ServiceAdvisorType.PYTHON, requestId,
+ saRunner, metaInfo, null));
StackAdvisorRequest request =
StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
.build();
@@ -213,8 +218,8 @@ public class StackAdvisorCommandTest {
String recommendationsArtifactsLifetime = "1w";
StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
- StackAdvisorCommand<TestResource> cmd = new TestStackAdvisorCommand(file,
recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType.PYTHON, 1,
- stackAdvisorRunner, ambariMetaInfo);
+ StackAdvisorCommand<TestResource> cmd = new TestStackAdvisorCommand(file,
recommendationsArtifactsLifetime,
+ ServiceInfo.ServiceAdvisorType.PYTHON, 1, stackAdvisorRunner,
ambariMetaInfo, null);
ObjectNode objectNode = (ObjectNode) cmd.mapper.readTree("{\"Versions\": "
+
"{\"stack_name\": \"stack\", \"stack_version\":\"1.0.0\"}}");
@@ -242,7 +247,7 @@ public class StackAdvisorCommandTest {
StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
StackAdvisorCommand<TestResource> cmd = new TestStackAdvisorCommand(file,
recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType.PYTHON, 1,
- stackAdvisorRunner, ambariMetaInfo);
+ stackAdvisorRunner, ambariMetaInfo, null);
ObjectNode objectNode = (ObjectNode) cmd.mapper.readTree("{\"Versions\": "
+
"{\"stack_name\": \"stack\", \"stack_version\":\"1.0.0\"}}");
@@ -264,7 +269,7 @@ public class StackAdvisorCommandTest {
StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
StackAdvisorCommand<TestResource> cmd = new TestStackAdvisorCommand(file,
recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType.PYTHON, 1,
- stackAdvisorRunner, ambariMetaInfo);
+ stackAdvisorRunner, ambariMetaInfo, null);
ObjectNode objectNode = (ObjectNode) cmd.mapper.readTree("{\"Versions\": "
+
"{\"stack_name\": \"stack\", \"stack_version\":\"1.0.0\"}}");
@@ -292,7 +297,7 @@ public class StackAdvisorCommandTest {
ServiceInfo.ServiceAdvisorType.PYTHON,
0,
mock(StackAdvisorRunner.class),
- mock(AmbariMetaInfo.class));
+ mock(AmbariMetaInfo.class), null);
when(ambariServerConfigurationHandler.getConfigurations()).thenReturn(storedConfig);
JsonNode servicesRootNode = json("{}");
command.populateAmbariConfiguration((ObjectNode)servicesRootNode);
@@ -308,7 +313,7 @@ public class StackAdvisorCommandTest {
ServiceInfo.ServiceAdvisorType.PYTHON,
0,
mock(StackAdvisorRunner.class),
- mock(AmbariMetaInfo.class));
+ mock(AmbariMetaInfo.class), null);
when(ambariServerConfigurationHandler.getConfigurations()).thenReturn(emptyMap());
JsonNode servicesRootNode = json("{}");
command.populateAmbariConfiguration((ObjectNode)servicesRootNode);
@@ -316,6 +321,74 @@ public class StackAdvisorCommandTest {
assertEquals(expectedLdapConfig, servicesRootNode);
}
+ /**
+ * Try to retrieve host info twice. The inner cache should be populated with
first usage (with handleRequest method calling).
+ * And for next info retrieving for the same host the saved value should be
used.
+ */
+ @Test
+ public void testHostInfoCachingSingleHost() throws StackAdvisorException {
+ File file = mock(File.class);
+ String recommendationsArtifactsLifetime = "1w";
+ StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
+ AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
+ Map<String, JsonNode> hostInfoCache = new HashMap<>();
+ TestStackAdvisorCommand command = spy(new TestStackAdvisorCommand(file,
recommendationsArtifactsLifetime,
+ ServiceInfo.ServiceAdvisorType.PYTHON, 1,
+ stackAdvisorRunner, ambariMetaInfo, hostInfoCache));
+
+ // in second handling case NPE will be fired during result processing
+ doReturn(Response.status(200).entity(String.format(SINGLE_HOST_RESPONSE,
"hostName1")).build())
+ .doReturn(null)
+ .when(command).handleRequest(any(HttpHeaders.class),
any(String.class), any(UriInfo.class), any(Request.Type.class),
+ any(MediaType.class), any(ResourceInstance.class));
+
+ StackAdvisorRequest request = StackAdvisorRequestBuilder.
+ forStack(null,
null).ofType(StackAdvisorRequest.StackAdvisorRequestType.CONFIGURATIONS).
+ forHosts(Arrays.asList(new String[]{"hostName1"})).
+ build();
+ String firstResponse = command.getHostsInformation(request);
+ assertEquals(String.format(SINGLE_HOST_RESPONSE, "hostName1"),
firstResponse);
+
+ String secondResponse = command.getHostsInformation(request);
+ assertEquals(String.format(SINGLE_HOST_RESPONSE, "hostName1"),
secondResponse);
+ }
+
+ /**
+ * Try to retrieve multiple hosts info twice. The inner cache should be
populated with first usage for first host (hostName1).
+ * For the next usage with the both hosts handleRequest should be used for
second host only.
+ */
+ @Test
+ public void testHostInfoCachingTwoHost() throws StackAdvisorException {
+ File file = mock(File.class);
+ String recommendationsArtifactsLifetime = "1w";
+ StackAdvisorRunner stackAdvisorRunner = mock(StackAdvisorRunner.class);
+ AmbariMetaInfo ambariMetaInfo = mock(AmbariMetaInfo.class);
+ Map<String, JsonNode> hostInfoCache = new HashMap<>();
+ TestStackAdvisorCommand command = spy(new TestStackAdvisorCommand(file,
recommendationsArtifactsLifetime,
+ ServiceInfo.ServiceAdvisorType.PYTHON, 1,
+ stackAdvisorRunner, ambariMetaInfo, hostInfoCache));
+
+ doReturn(Response.status(200).entity(String.format(SINGLE_HOST_RESPONSE,
"hostName1")).build())
+
.doReturn(Response.status(200).entity(String.format(SINGLE_HOST_RESPONSE,
"hostName2")).build())
+ .doReturn(null)
+ .when(command).handleRequest(any(HttpHeaders.class),
any(String.class), any(UriInfo.class), any(Request.Type.class),
+ any(MediaType.class), any(ResourceInstance.class));
+
+ StackAdvisorRequest request = StackAdvisorRequestBuilder.
+ forStack(null,
null).ofType(StackAdvisorRequest.StackAdvisorRequestType.CONFIGURATIONS).
+ forHosts(Arrays.asList(new String[]{"hostName1"})).
+ build();
+ String firstResponse = command.getHostsInformation(request);
+ assertEquals(String.format(SINGLE_HOST_RESPONSE, "hostName1"),
firstResponse);
+
+ request = StackAdvisorRequestBuilder.
+ forStack(null,
null).ofType(StackAdvisorRequest.StackAdvisorRequestType.CONFIGURATIONS).
+ forHosts(Arrays.asList(new String[]{"hostName1", "hostName2"})).
+ build();
+ String secondResponse = command.getHostsInformation(request);
+ assertEquals(String.format(TWO_HOST_RESPONSE, "hostName1", "hostName2"),
secondResponse);
+ }
+
private static String jsonString(Object obj) throws IOException {
return new ObjectMapper().writeValueAsString(obj);
}
@@ -343,8 +416,9 @@ public class StackAdvisorCommandTest {
class TestStackAdvisorCommand extends StackAdvisorCommand<TestResource> {
public TestStackAdvisorCommand(File recommendationsDir, String
recommendationsArtifactsLifetime, ServiceInfo.ServiceAdvisorType
serviceAdvisorType,
- int requestId, StackAdvisorRunner saRunner,
AmbariMetaInfo metaInfo) {
- super(recommendationsDir, recommendationsArtifactsLifetime,
serviceAdvisorType, requestId, saRunner, metaInfo,
ambariServerConfigurationHandler);
+ int requestId, StackAdvisorRunner saRunner,
AmbariMetaInfo metaInfo, Map<String, JsonNode> hostInfoCache) {
+ super(recommendationsDir, recommendationsArtifactsLifetime,
serviceAdvisorType, requestId, saRunner, metaInfo,
+ ambariServerConfigurationHandler, hostInfoCache);
}
@Override
diff --git
a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
index 1567e38..c2e43e9 100644
---
a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
+++
b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
@@ -52,6 +52,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import javax.persistence.EntityManager;
@@ -2379,11 +2380,11 @@ public class KerberosHelperTest extends EasyMockSupport
{
services.put("SERVICE3", service3);
Map<String, Set<String>> serviceComponentHostMap = new HashMap<>();
- serviceComponentHostMap.put("COMPONENT1A", Collections.singleton("hostA"));
- serviceComponentHostMap.put("COMPONENT1B", new
HashSet<>(Arrays.asList("hostB", "hostC")));
- serviceComponentHostMap.put("COMPONENT2A", Collections.singleton("hostA"));
- serviceComponentHostMap.put("COMPONENT2B", new
HashSet<>(Arrays.asList("hostB", "hostC")));
- serviceComponentHostMap.put("COMPONEN3A", Collections.singleton("hostA"));
+ serviceComponentHostMap.put("COMPONENT1A", new
TreeSet<>(Arrays.asList("hostA")));
+ serviceComponentHostMap.put("COMPONENT1B", new
TreeSet<>(Arrays.asList("hostB", "hostC")));
+ serviceComponentHostMap.put("COMPONENT2A", new
TreeSet<>(Arrays.asList("hostA")));
+ serviceComponentHostMap.put("COMPONENT2B", new
TreeSet<>(Arrays.asList("hostB", "hostC")));
+ serviceComponentHostMap.put("COMPONEN3A", new
TreeSet<>(Arrays.asList("hostA")));
final Cluster cluster = createMockCluster("c1", hosts,
SecurityType.KERBEROS, krb5ConfConfig, kerberosEnvConfig);
expect(cluster.getServices()).andReturn(services).anyTimes();
diff --git
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
index 8d02821..4e46a83 100644
---
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
+++
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RecommendationResourceProviderTest.java
@@ -18,12 +18,177 @@
package org.apache.ambari.server.controller.internal;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.partialMockBuilder;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import
org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
+import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorResponse;
+import
org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+import org.apache.ambari.server.state.Clusters;
import org.junit.Test;
public class RecommendationResourceProviderTest {
@Test
- public void testCreateResources() throws Exception {
+ public void testCreateConfigurationResources() throws Exception {
+ Set<String> hosts = new HashSet<>(Arrays.asList(new String[]{"hostName1",
"hostName2", "hostName3"}));
+ Set<String> services = new HashSet<>(Arrays.asList(new
String[]{"serviceName1", "serviceName2", "serviceName3"}));
+ RequestStatus requestStatus = testCreateResources(hosts, services,
StackAdvisorRequest.StackAdvisorRequestType.CONFIGURATIONS);
+
+ assertFalse(requestStatus == null);
+ assertEquals(1, requestStatus.getAssociatedResources().size());
+ assertEquals(Resource.Type.Recommendation,
requestStatus.getAssociatedResources().iterator().next().getType());
+
+ Map<String, Map<String, Object>> propertiesMap =
requestStatus.getAssociatedResources().iterator().next().getPropertiesMap();
+ assertEquals(2, propertiesMap.size());
+ assertTrue(propertiesMap.containsKey("recommendations"));
+
assertTrue(propertiesMap.containsKey("recommendations/blueprint/configurations"));
+
+ assertEquals(1, propertiesMap.get("recommendations").size());
+
assertTrue(propertiesMap.get("recommendations").containsKey("config-groups"));
+ assertNotNull(propertiesMap.get("recommendations").get("config-groups"));
+
+ assertEquals(0,
propertiesMap.get("recommendations/blueprint/configurations").size());
+ }
+
+ @Test
+ public void testCreateNotConfigurationResources() throws Exception {
+ Set<String> hosts = new HashSet<>(Arrays.asList(new String[]{"hostName1",
"hostName2", "hostName3"}));
+ Set<String> services = new HashSet<>(Arrays.asList(new
String[]{"serviceName1", "serviceName2", "serviceName3"}));
+ RequestStatus requestStatus = testCreateResources(hosts, services,
StackAdvisorRequest.StackAdvisorRequestType.HOST_GROUPS);
+
+ assertFalse(requestStatus == null);
+ assertEquals(1, requestStatus.getAssociatedResources().size());
+ assertEquals(Resource.Type.Recommendation,
requestStatus.getAssociatedResources().iterator().next().getType());
+
+ Map<String, Map<String, Object>> propertiesMap =
requestStatus.getAssociatedResources().iterator().next().getPropertiesMap();
+ assertEquals(7, propertiesMap.size());
+ assertTrue(propertiesMap.containsKey(""));
+ assertTrue(propertiesMap.containsKey("Recommendation"));
+ assertTrue(propertiesMap.containsKey("Versions"));
+ assertTrue(propertiesMap.containsKey("recommendations"));
+ assertTrue(propertiesMap.containsKey("recommendations/blueprint"));
+
assertTrue(propertiesMap.containsKey("recommendations/blueprint/configurations"));
+
assertTrue(propertiesMap.containsKey("recommendations/blueprint_cluster_binding"));
+
+ assertEquals(2, propertiesMap.get("").size());
+ assertTrue(propertiesMap.get("").containsKey("hosts"));
+ assertTrue(propertiesMap.get("").containsKey("services"));
+ assertEquals(hosts, propertiesMap.get("").get("hosts"));
+ assertEquals(services, propertiesMap.get("").get("services"));
+
+ assertEquals(1, propertiesMap.get("Recommendation").size());
+ assertTrue(propertiesMap.get("Recommendation").containsKey("id"));
+ assertEquals(1, propertiesMap.get("Recommendation").get("id"));
+
+ assertEquals(2, propertiesMap.get("Versions").size());
+ assertTrue(propertiesMap.get("Versions").containsKey("stack_name"));
+ assertTrue(propertiesMap.get("Versions").containsKey("stack_version"));
+ assertEquals("stackName", propertiesMap.get("Versions").get("stack_name"));
+ assertEquals("stackVersion",
propertiesMap.get("Versions").get("stack_version"));
+
+ assertEquals(1, propertiesMap.get("recommendations").size());
+
assertTrue(propertiesMap.get("recommendations").containsKey("config-groups"));
+ assertNotNull(propertiesMap.get("recommendations").get("config-groups"));
+
+ assertEquals(1, propertiesMap.get("recommendations/blueprint").size());
+
assertTrue(propertiesMap.get("recommendations/blueprint").containsKey("host_groups"));
+
assertNotNull(propertiesMap.get("recommendations/blueprint").get("host_groups"));
+
+ assertEquals(0,
propertiesMap.get("recommendations/blueprint/configurations").size());
+
+ assertEquals(1,
propertiesMap.get("recommendations/blueprint_cluster_binding").size());
+
assertTrue(propertiesMap.get("recommendations/blueprint_cluster_binding").containsKey("host_groups"));
+
assertNotNull(propertiesMap.get("recommendations/blueprint_cluster_binding").get("host_groups"));
+
+ }
+
+ private RequestStatus testCreateResources(Set<String> hosts, Set<String>
services,
+
StackAdvisorRequest.StackAdvisorRequestType type) throws
+ NoSuchParentResourceException, ResourceAlreadyExistsException,
+ UnsupportedPropertyException, SystemException, StackAdvisorException,
AmbariException {
+ StackAdvisorHelper stackAdvisorHelper =
createMock(StackAdvisorHelper.class);
+ Configuration configuration = createMock(Configuration.class);
+ Clusters clusters = createMock(Clusters.class);
+ AmbariMetaInfo ambariMetaInfo = createMock(AmbariMetaInfo.class);
+
+ RecommendationResourceProvider provider =
partialMockBuilder(RecommendationResourceProvider.class)
+ .withConstructor(AmbariManagementController.class)
+ .withArgs(createMock(AmbariManagementController.class))
+ .addMockedMethod("prepareStackAdvisorRequest", Request.class)
+ .createMock();
+ RecommendationResourceProvider.init(stackAdvisorHelper, configuration,
clusters, ambariMetaInfo);
+
+ StackAdvisorRequest stackAdvisorRequest =
StackAdvisorRequest.StackAdvisorRequestBuilder.
+ forStack(null, null).ofType(type).
+
withConfigsResponse(StackAdvisorRequest.StackAdvisorRequestType.CONFIGURATIONS.equals(type)).
+ build();
+
+ Request request = createMock(Request.class);
+
expect(provider.prepareStackAdvisorRequest(eq(request))).andReturn(stackAdvisorRequest);
+
+ RecommendationResponse response = new RecommendationResponse();
+ RecommendationResponse.Recommendation recommendation = new
RecommendationResponse.Recommendation();
+
+ recommendation.setConfigGroups(new HashSet<>());
+
+ RecommendationResponse.Blueprint blueprint = new
RecommendationResponse.Blueprint();
+ blueprint.setConfigurations(new HashMap<>());
+ blueprint.setHostGroups(new HashSet<>());
+ recommendation.setBlueprint(blueprint);
+
+ RecommendationResponse.BlueprintClusterBinding blueprintClusterBinding =
new RecommendationResponse.BlueprintClusterBinding();
+ blueprintClusterBinding.setHostGroups(new HashSet<>());
+ recommendation.setBlueprintClusterBinding(blueprintClusterBinding);
+
+ response.setRecommendations(recommendation);
+
+ response.setId(1);
+
+ StackAdvisorResponse.Version version = new StackAdvisorResponse.Version();
+ version.setStackName("stackName");
+ version.setStackVersion("stackVersion");
+ response.setVersion(version);
+
+ response.setHosts(hosts);
+ response.setServices(services);
+
+
expect(stackAdvisorHelper.recommend(anyObject(StackAdvisorRequest.class))).andReturn(response).anyTimes();
+
+ replay(provider, request, stackAdvisorHelper);
+
+ RequestStatus requestStatus = provider.createResources(request);
+
+ verify(provider, request, stackAdvisorHelper);
+ return requestStatus;
}
}
diff --git
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ValidationResourceProviderTest.java
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ValidationResourceProviderTest.java
index 34337ae..38cf6fe 100644
---
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ValidationResourceProviderTest.java
+++
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ValidationResourceProviderTest.java
@@ -30,6 +30,7 @@ import java.util.Collections;
import java.util.Map;
import java.util.Set;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorHelper;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
import
org.apache.ambari.server.api.services.stackadvisor.validations.ValidationResponse;
@@ -38,6 +39,7 @@ import
org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.spi.Request;
import org.apache.ambari.server.controller.spi.RequestStatus;
import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.state.Clusters;
import org.junit.Test;
public class ValidationResourceProviderTest {
@@ -60,7 +62,7 @@ public class ValidationResourceProviderTest {
doReturn(3).when(response).getId();
doReturn(version).when(response).getVersion();
doReturn(response).when(saHelper).validate(any(StackAdvisorRequest.class));
- ValidationResourceProvider.init(saHelper, configuration);
+ ValidationResourceProvider.init(saHelper, configuration,
mock(Clusters.class), mock(AmbariMetaInfo.class));
RequestStatus status = provider.createResources(request);
diff --git a/ambari-web/app/controllers/main/service/info/configs.js
b/ambari-web/app/controllers/main/service/info/configs.js
index 54fd484..7b93f9a 100644
--- a/ambari-web/app/controllers/main/service/info/configs.js
+++ b/ambari-web/app/controllers/main/service/info/configs.js
@@ -24,6 +24,12 @@ App.MainServiceInfoConfigsController =
Em.Controller.extend(App.AddSecurityConfi
App.ConfigsComparator, App.ComponentActionsByConfigs, {
name: 'mainServiceInfoConfigsController',
+
+ /**
+ * Recommendations data will be completed on server side,
+ * UI doesn't have to send all cluster data as hosts, configurations,
config-groups, etc.
+ */
+ isRecommendationsAutoComplete: true,
isHostsConfigsPage: false,
diff --git a/ambari-web/app/mixins/common/configs/enhanced_configs.js
b/ambari-web/app/mixins/common/configs/enhanced_configs.js
index bf091fb..9bd1ba2 100644
--- a/ambari-web/app/mixins/common/configs/enhanced_configs.js
+++ b/ambari-web/app/mixins/common/configs/enhanced_configs.js
@@ -221,11 +221,12 @@ App.EnhancedConfigsMixin =
Em.Mixin.create(App.ConfigWithOverrideRecommendationP
var updateDependencies = Em.isArray(changedConfigs) &&
changedConfigs.length > 0;
var stepConfigs = this.get('stepConfigs');
var requiredTags = [];
+ const isAutoComplete = Boolean(this.get('isRecommendationsAutoComplete'));
if (updateDependencies || Em.isNone(this.get('recommendationsConfigs'))) {
- var recommendations = this.get('hostGroups');
- var dataToSend = this.getConfigRecommendationsParams(updateDependencies,
changedConfigs);
- this.modifyRecommendationConfigGroups(recommendations);
+ var recommendations = isAutoComplete ? {} : this.get('hostGroups');
+ var dataToSend = this.getConfigRecommendationsParams(updateDependencies,
changedConfigs, isAutoComplete);
+ this.modifyRecommendationConfigGroups(recommendations, isAutoComplete);
if (!stepConfigs.someProperty('serviceName', 'MISC')) {
requiredTags.push({site: 'cluster-env', serviceName: 'MISC'});
@@ -265,8 +266,16 @@ App.EnhancedConfigsMixin =
Em.Mixin.create(App.ConfigWithOverrideRecommendationP
* @param stepConfigs
*/
addRecommendationRequestParams: function(recommendations, dataToSend,
stepConfigs) {
- recommendations.blueprint.configurations =
blueprintUtils.buildConfigsJSON(stepConfigs);
+ const isAutoComplete = Boolean(this.get('isRecommendationsAutoComplete'));
+ if (!isAutoComplete) {
+ recommendations.blueprint.configurations =
blueprintUtils.buildConfigsJSON(stepConfigs);
+ }
dataToSend.recommendations = recommendations;
+ dataToSend.serviceName = this.get('content.serviceName');
+ dataToSend.clusterId = App.get('clusterId');
+ dataToSend.autoComplete = String(isAutoComplete);
+ // configsResponse - tells server to return only configurations in
recommendations call
+ dataToSend.configsResponse = String(isAutoComplete);
},
/**
@@ -289,12 +298,13 @@ App.EnhancedConfigsMixin =
Em.Mixin.create(App.ConfigWithOverrideRecommendationP
/**
*
* @param {object} recommendations
+ * @param {boolean} isAutoComplete
*/
- modifyRecommendationConfigGroups: function(recommendations) {
+ modifyRecommendationConfigGroups: function(recommendations, isAutoComplete) {
var configGroup = this.get('selectedConfigGroup');
if (configGroup && !configGroup.get('isDefault') &&
configGroup.get('hosts.length') > 0) {
- recommendations.config_groups =
[this.buildConfigGroupJSON(this.get('selectedService.configs'), configGroup)];
+ recommendations.config_groups =
[this.buildConfigGroupJSON(this.get('selectedService.configs'), configGroup,
isAutoComplete)];
} else {
delete recommendations.config_groups;
}
@@ -304,13 +314,14 @@ App.EnhancedConfigsMixin =
Em.Mixin.create(App.ConfigWithOverrideRecommendationP
*
* @param {boolean} updateDependencies
* @param {Array} changedConfigs
+ * @param {boolean} isAutoComplete
* @returns {{recommend: string, hosts: *, services: *,
changed_configurations: *}}
*/
- getConfigRecommendationsParams: function(updateDependencies, changedConfigs)
{
+ getConfigRecommendationsParams: function(updateDependencies, changedConfigs,
isAutoComplete) {
return {
recommend: updateDependencies ? 'configuration-dependencies' :
'configurations',
- hosts: this.get('hostNames'),
- services: this.get('serviceNames'),
+ hosts: isAutoComplete ? undefined : this.get('hostNames'),
+ services: isAutoComplete ? undefined : this.get('serviceNames'),
changed_configurations: updateDependencies ? changedConfigs : undefined
};
},
@@ -378,10 +389,16 @@ App.EnhancedConfigsMixin =
Em.Mixin.create(App.ConfigWithOverrideRecommendationP
* generates JSON with config group info to send it for recommendations
* @param configs
* @param configGroup
+ * @param {boolean} isAutoComplete
* @returns {{configurations: Object[], hosts: string[]}}
*/
- buildConfigGroupJSON: function(configs, configGroup) {
+ buildConfigGroupJSON: function(configs, configGroup, isAutoComplete) {
Em.assert('configGroup can\'t be null', configGroup);
+ if (isAutoComplete) {
+ return {
+ group_id: Number(configGroup.get('id'))
+ }
+ }
var hosts = configGroup.get('hosts');
var configurations = {};
var overrides = configs.forEach(function(cp) {
@@ -396,7 +413,8 @@ App.EnhancedConfigsMixin =
Em.Mixin.create(App.ConfigWithOverrideRecommendationP
});
return {
configurations: [configurations],
- hosts: hosts
+ hosts: hosts,
+ group_id: Number(configGroup.get('id'))
}
},
diff --git a/ambari-web/test/mixins/common/configs/enhanced_configs_test.js
b/ambari-web/test/mixins/common/configs/enhanced_configs_test.js
index 1cf7620..1d6f6d2 100644
--- a/ambari-web/test/mixins/common/configs/enhanced_configs_test.js
+++ b/ambari-web/test/mixins/common/configs/enhanced_configs_test.js
@@ -25,7 +25,12 @@ describe('App.EnhancedConfigsMixin', function () {
var mixinClass = Em.Controller.extend(App.EnhancedConfigsMixin);
beforeEach(function () {
- mixin = Em.Object.create(App.EnhancedConfigsMixin);
+ mixin = Em.Object.create(App.EnhancedConfigsMixin, {
+ isRecommendationsAutoComplete: false,
+ content: Em.Object.create({
+ serviceName: 'S1'
+ })
+ });
});
describe('#removeCurrentFromDependentList()', function () {
@@ -54,6 +59,7 @@ describe('App.EnhancedConfigsMixin', function () {
it('generates JSON based on config group info', function () {
var configGroup = Em.Object.create({
name: 'group1',
+ id: '1',
isDefault: false,
hosts: ['host1', 'host2']
});
@@ -94,7 +100,8 @@ describe('App.EnhancedConfigsMixin', function () {
}
}
],
- "hosts": ['host1', 'host2']
+ "hosts": ['host1', 'host2'],
+ "group_id": 1
})
});
@@ -575,10 +582,12 @@ describe('App.EnhancedConfigsMixin', function () {
beforeEach(function() {
sinon.stub(blueprintUtils, 'buildConfigsJSON').returns([{}]);
+ sinon.stub(App, 'get').returns(1);
});
afterEach(function() {
blueprintUtils.buildConfigsJSON.restore();
+ App.get.restore();
});
it("recommendations should be set", function () {
@@ -591,7 +600,11 @@ describe('App.EnhancedConfigsMixin', function () {
{}
]
}
- }
+ },
+ "autoComplete": "false",
+ "configsResponse": "false",
+ "serviceName": "S1",
+ "clusterId": 1
});
});
});
@@ -775,11 +788,25 @@ describe('App.EnhancedConfigsMixin', function () {
{
configs: [],
configGroup: Em.Object.create({
- hosts: []
+ hosts: [],
+ id: '1'
}),
+ autoComplete: false,
expected: {
configurations: [{}],
- hosts: []
+ hosts: [],
+ group_id: 1
+ }
+ },
+ {
+ configs: [],
+ configGroup: Em.Object.create({
+ hosts: [],
+ id: '1'
+ }),
+ autoComplete: true,
+ expected: {
+ group_id: 1
}
},
{
@@ -787,11 +814,14 @@ describe('App.EnhancedConfigsMixin', function () {
overrides: null
})],
configGroup: Em.Object.create({
- hosts: []
+ hosts: [],
+ id: '2'
}),
+ autoComplete: false,
expected: {
configurations: [{}],
- hosts: []
+ hosts: [],
+ group_id: 2
}
},
{
@@ -804,11 +834,14 @@ describe('App.EnhancedConfigsMixin', function () {
})],
configGroup: Em.Object.create({
name: 'g1',
+ id: '3',
hosts: []
}),
+ autoComplete: false,
expected: {
configurations: [{}],
- hosts: []
+ hosts: [],
+ group_id: 3
}
},
{
@@ -823,8 +856,10 @@ describe('App.EnhancedConfigsMixin', function () {
})],
configGroup: Em.Object.create({
name: 'g1',
- hosts: []
+ hosts: [],
+ id: '4'
}),
+ autoComplete: false,
expected: {
configurations: [{
"tag1": {
@@ -833,15 +868,17 @@ describe('App.EnhancedConfigsMixin', function () {
}
}
}],
- hosts: []
+ hosts: [],
+ group_id: 4
}
}
];
testCases.forEach(function(test) {
- it("configs = " + test.configs +
- "configGroup = " + JSON.stringify(test.configGroup), function() {
- expect(mixin.buildConfigGroupJSON(test.configs,
test.configGroup)).to.be.eql(test.expected);
+ it("configs = " + JSON.stringify(test.configs) +
+ "configGroup = " + JSON.stringify(test.configGroup) +
+ "autoComplete = " + test.autoComplete, function() {
+ expect(mixin.buildConfigGroupJSON(test.configs, test.configGroup,
test.autoComplete)).to.be.eql(test.expected);
});
});
});