You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by mp...@apache.org on 2018/11/20 11:54:09 UTC
[ambari] branch trunk updated: AMBARI-24876. BE: Performance Tune
service Configs Pages. (#2619)
This is an automated email from the ASF dual-hosted git repository.
mpapirkovskyy pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push:
new 4dde32c AMBARI-24876. BE: Performance Tune service Configs Pages. (#2619)
4dde32c is described below
commit 4dde32c21251ad9485869f2976f0f4a95aa0e43d
Author: Myroslav Papirkovskyi <mp...@apache.org>
AuthorDate: Tue Nov 20 13:54:04 2018 +0200
AMBARI-24876. BE: Performance Tune service Configs Pages. (#2619)
* AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
* AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
* AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
* AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
* AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
* AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
* AMBARI-24876. BE: Performance Tune service Configs Pages. (mpapirkovskyy)
* AMBARI-24876. BE: Performance Tune service Configs Pages. (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 | 51 ++++
.../StackAdvisorBlueprintProcessor.java | 51 ++--
.../services/stackadvisor/StackAdvisorHelper.java | 83 +++++-
.../services/stackadvisor/StackAdvisorRequest.java | 82 ++++--
.../ComponentLayoutRecommendationCommand.java | 3 +-
...igurationDependenciesRecommendationCommand.java | 5 +-
.../ConfigurationRecommendationCommand.java | 12 +-
.../stackadvisor/commands/StackAdvisorCommand.java | 96 +++++--
.../controller/AmbariManagementControllerImpl.java | 6 +-
.../ambari/server/controller/AmbariServer.java | 2 +-
.../server/controller/KerberosHelperImpl.java | 21 +-
...erverStackAdvisorAwareConfigurationHandler.java | 12 +-
.../controller/internal/HostResourceProvider.java | 3 +-
.../internal/RecommendationResourceProvider.java | 75 +++---
.../internal/StackAdvisorResourceProvider.java | 280 +++++++++++++++++----
.../events/listeners/hosts/HostUpdateListener.java | 12 +-
.../org/apache/ambari/server/state/Cluster.java | 11 +-
.../ambari/server/state/cluster/ClusterImpl.java | 17 +-
.../topology/STOMPComponentsDeleteHandler.java | 4 +-
.../server/agent/stomp/TopologyClusterTest.java | 186 ++++++++++++++
.../StackAdvisorBlueprintProcessorTest.java | 13 +-
.../stackadvisor/StackAdvisorHelperTest.java | 149 ++++++++++-
.../ConfigurationRecommendationCommandTest.java | 15 +-
.../commands/StackAdvisorCommandTest.java | 98 +++++++-
.../server/controller/KerberosHelperTest.java | 18 +-
.../AmbariServerSSOConfigurationHandlerTest.java | 8 +-
.../RecommendationResourceProviderTest.java | 170 ++++++++++++-
.../internal/StackAdvisorResourceProviderTest.java | 9 +-
.../internal/ValidationResourceProviderTest.java | 4 +-
.../metrics/RestMetricsPropertyProviderTest.java | 39 +--
.../ambari/server/state/cluster/ClusterTest.java | 10 +-
.../app/controllers/main/service/info/configs.js | 6 +
.../app/mixins/common/configs/enhanced_configs.js | 40 ++-
.../mixins/common/configs/enhanced_configs_test.js | 63 ++++-
39 files changed, 1428 insertions(+), 319 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 8caa57a..4eb877c 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;
@@ -61,6 +63,9 @@ public class TopologyHolder extends AgentClusterDataHolder<TopologyUpdateEvent>
private Clusters clusters;
@Inject
+ private StackAdvisorHelper stackAdvisorHelper;
+
+ @Inject
public TopologyHolder(AmbariEventPublisher ambariEventPublisher) {
ambariEventPublisher.register(this);
}
@@ -100,13 +105,17 @@ 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();
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))
@@ -142,7 +151,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();
@@ -152,25 +161,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..6a616d8
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/dto/TopologyUpdateHandlingReport.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.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..54b7e1f 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
@@ -19,12 +19,15 @@
package org.apache.ambari.server.api.services.stackadvisor;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+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;
@@ -44,7 +47,6 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
import com.google.inject.Singleton;
/**
@@ -64,10 +66,10 @@ public class StackAdvisorBlueprintProcessor {
stackAdvisorHelper = instance;
}
- private static final Map<String, String> userContext;
+ private static final SortedMap<String, String> userContext;
static
{
- userContext = new HashMap<>();
+ userContext = new TreeMap<>();
userContext.put("operation", "ClusterCreate");
}
@@ -81,7 +83,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);
@@ -90,9 +92,9 @@ public class StackAdvisorBlueprintProcessor {
private StackAdvisorRequest createStackAdvisorRequest(ClusterTopology clusterTopology, StackAdvisorRequestType requestType) {
Stack stack = clusterTopology.getBlueprint().getStack();
- Map<String, Set<String>> hgComponentsMap = gatherHostGroupComponents(clusterTopology);
- Map<String, Set<String>> hgHostsMap = gatherHostGroupBindings(clusterTopology);
- Map<String, Set<String>> componentHostsMap = gatherComponentsHostsMap(hgComponentsMap,
+ SortedMap<String, SortedSet<String>> hgComponentsMap = gatherHostGroupComponents(clusterTopology);
+ SortedMap<String, SortedSet<String>> hgHostsMap = gatherHostGroupBindings(clusterTopology);
+ SortedMap<String, SortedSet<String>> componentHostsMap = gatherComponentsHostsMap(hgComponentsMap,
hgHostsMap);
return StackAdvisorRequest.StackAdvisorRequestBuilder
.forStack(stack.getName(), stack.getVersion())
@@ -107,46 +109,47 @@ public class StackAdvisorBlueprintProcessor {
.build();
}
- private Map<String, Set<String>> gatherHostGroupBindings(ClusterTopology clusterTopology) {
- Map<String, Set<String>> hgBindngs = Maps.newHashMap();
+ private SortedMap<String, SortedSet<String>> gatherHostGroupBindings(ClusterTopology clusterTopology) {
+ SortedMap<String, SortedSet<String>> hgBindngs = Maps.newTreeMap();
for (Map.Entry<String, HostGroupInfo> hgEnrty: clusterTopology.getHostGroupInfo().entrySet()) {
- hgBindngs.put(hgEnrty.getKey(), Sets.newCopyOnWriteArraySet(hgEnrty.getValue().getHostNames()));
+ hgBindngs.put(hgEnrty.getKey(), new TreeSet<>(hgEnrty.getValue().getHostNames()));
}
return hgBindngs;
}
- private Map<String, Set<String>> gatherHostGroupComponents(ClusterTopology clusterTopology) {
- Map<String, Set<String>> hgComponentsMap = Maps.newHashMap();
+ private SortedMap<String, SortedSet<String>> gatherHostGroupComponents(ClusterTopology clusterTopology) {
+ SortedMap<String, SortedSet<String>> hgComponentsMap = Maps.newTreeMap();
for (Map.Entry<String, HostGroup> hgEnrty: clusterTopology.getBlueprint().getHostGroups().entrySet()) {
- hgComponentsMap.put(hgEnrty.getKey(), Sets.newCopyOnWriteArraySet(hgEnrty.getValue().getComponentNames()));
+ hgComponentsMap.put(hgEnrty.getKey(), new TreeSet<>(hgEnrty.getValue().getComponentNames()));
}
return hgComponentsMap;
}
- private Map<String, Map<String, Map<String, String>>> calculateConfigs(ClusterTopology clusterTopology) {
- Map<String, Map<String, Map<String, String>>> result = Maps.newHashMap();
+ private SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculateConfigs(ClusterTopology clusterTopology) {
+ SortedMap<String, SortedMap<String, SortedMap<String, String>>> result = Maps.newTreeMap();
Map<String, Map<String, String>> fullProperties = clusterTopology.getConfiguration().getFullProperties();
for (Map.Entry<String, Map<String, String>> siteEntry : fullProperties.entrySet()) {
- Map<String, Map<String, String>> propsMap = Maps.newHashMap();
- propsMap.put("properties", siteEntry.getValue());
+ SortedMap<String, SortedMap<String, String>> propsMap = Maps.newTreeMap();
+ propsMap.put("properties", new TreeMap<>(siteEntry.getValue()));
result.put(siteEntry.getKey(), propsMap);
}
return result;
}
- private Map<String, Set<String>> gatherComponentsHostsMap(Map<String, Set<String>> hostGroups, Map<String, Set<String>> bindingHostGroups) {
- Map<String, Set<String>> componentHostsMap = new HashMap<>();
+ private SortedMap<String, SortedSet<String>> gatherComponentsHostsMap(SortedMap<String, SortedSet<String>> hostGroups,
+ SortedMap<String, SortedSet<String>> bindingHostGroups) {
+ SortedMap<String, SortedSet<String>> componentHostsMap = new TreeMap<>();
if (null != bindingHostGroups && null != hostGroups) {
- for (Map.Entry<String, Set<String>> hgComponents : hostGroups.entrySet()) {
+ for (Map.Entry<String, SortedSet<String>> hgComponents : hostGroups.entrySet()) {
String hgName = hgComponents.getKey();
Set<String> components = hgComponents.getValue();
Set<String> hosts = bindingHostGroups.get(hgName);
if (hosts != null) {
for (String component : components) {
- Set<String> componentHosts = componentHostsMap.get(component);
+ SortedSet<String> componentHosts = componentHostsMap.get(component);
if (componentHosts == null) { // if was not initialized
- componentHosts = new HashSet<>();
+ componentHosts = new TreeSet<>();
componentHostsMap.put(component, componentHosts);
}
componentHosts.addAll(hosts);
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 d3f2072..e92bef8 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;
@@ -36,7 +42,11 @@ import org.apache.ambari.server.api.services.stackadvisor.validations.Validation
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.controller.internal.AmbariServerConfigurationHandler;
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;
@@ -44,25 +54,33 @@ import com.google.inject.Singleton;
@Singleton
public class StackAdvisorHelper {
+ protected static Log LOG = LogFactory.getLog(StackAdvisorHelper.class);
+
private File recommendationsDir;
private String recommendationsArtifactsLifetime;
private int recommendationsArtifactsRolloverMax;
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();
this.saRunner = saRunner;
this.metaInfo = metaInfo;
this.ambariServerConfigurationHandler = ambariServerConfigurationHandler;
+ this.gson = gson;
}
/**
@@ -115,7 +133,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.
@@ -125,7 +143,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("Calling stack advisor with hash: %s, service: %s", hash, request.getServiceName()));
+ response = configsRecommendationResponse.computeIfAbsent(hash, h -> {
+ try {
+ LOG.info(String.format("Invoking configuration stack advisor command 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 {
@@ -138,16 +189,16 @@ 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.LDAP_CONFIGURATIONS) {
command = new ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS_FOR_LDAP, 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);
@@ -188,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 ea4a4e0..de6448b 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
@@ -21,11 +21,11 @@ package org.apache.ambari.server.api.services.stackadvisor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
import org.apache.ambari.server.state.ChangedConfigInfo;
@@ -38,20 +38,23 @@ import com.google.common.base.Preconditions;
*/
public class StackAdvisorRequest {
+ private Long clusterId;
+ private String serviceName;
private String stackName;
private String stackVersion;
private StackAdvisorRequestType requestType;
private List<String> hosts = new ArrayList<>();
private Collection<String> services = new ArrayList<>();
- private Map<String, Set<String>> componentHostsMap = new HashMap<>();
- private Map<String, Set<String>> hostComponents = new HashMap<>();
- private Map<String, Set<String>> hostGroupBindings = new HashMap<>();
- private Map<String, Map<String, Map<String, String>>> configurations = new HashMap<>();
+ private SortedMap<String, SortedSet<String>> componentHostsMap = new TreeMap<>();
+ private SortedMap<String, SortedSet<String>> hostComponents = new TreeMap<>();
+ private SortedMap<String, SortedSet<String>> hostGroupBindings = new TreeMap<>();
+ private SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations = new TreeMap<>();
private List<ChangedConfigInfo> changedConfigurations = new LinkedList<>();
- private Set<RecommendationResponse.ConfigGroup> configGroups;
- private Map<String, String> userContext = new HashMap<>();
- private Map<String, Object> ldapConfig = new HashMap<>();
+ private SortedSet<RecommendationResponse.ConfigGroup> configGroups;
+ private SortedMap<String, String> userContext = new TreeMap<>();
+ private SortedMap<String, Object> ldapConfig = new TreeMap<>();
private Boolean gplLicenseAccepted;
+ private Boolean configsResponse = false;
public String getStackName() {
return stackName;
@@ -73,7 +76,7 @@ public class StackAdvisorRequest {
return services;
}
- public Map<String, Set<String>> getComponentHostsMap() {
+ public SortedMap<String, SortedSet<String>> getComponentHostsMap() {
return componentHostsMap;
}
@@ -85,19 +88,19 @@ public class StackAdvisorRequest {
return StringUtils.join(services, ",");
}
- public Map<String, Set<String>> getHostComponents() {
+ public SortedMap<String, SortedSet<String>> getHostComponents() {
return hostComponents;
}
- public Map<String, Set<String>> getHostGroupBindings() {
+ public SortedMap<String, SortedSet<String>> getHostGroupBindings() {
return hostGroupBindings;
}
- public Map<String, Map<String, Map<String, String>>> getConfigurations() {
+ public SortedMap<String, SortedMap<String, SortedMap<String, String>>> getConfigurations() {
return configurations;
}
- public Map<String, Object> getLdapConfig() { return ldapConfig; }
+ public SortedMap<String, Object> getLdapConfig() { return ldapConfig; }
public List<ChangedConfigInfo> getChangedConfigurations() {
return changedConfigurations;
@@ -107,22 +110,30 @@ public class StackAdvisorRequest {
this.changedConfigurations = changedConfigurations;
}
- public Map<String, String> getUserContext() {
+ public SortedMap<String, String> getUserContext() {
return this.userContext;
}
- public void setUserContext(Map<String, String> userContext) {
+ public void setUserContext(SortedMap<String, String> userContext) {
this.userContext = userContext;
}
- public Set<RecommendationResponse.ConfigGroup> getConfigGroups() {
+ public SortedSet<RecommendationResponse.ConfigGroup> getConfigGroups() {
return configGroups;
}
- public void setConfigGroups(Set<RecommendationResponse.ConfigGroup> configGroups) {
+ public void setConfigGroups(SortedSet<RecommendationResponse.ConfigGroup> configGroups) {
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;
@@ -162,24 +177,24 @@ public class StackAdvisorRequest {
}
public StackAdvisorRequestBuilder withComponentHostsMap(
- Map<String, Set<String>> componentHostsMap) {
+ SortedMap<String, SortedSet<String>> componentHostsMap) {
this.instance.componentHostsMap = componentHostsMap;
return this;
}
- public StackAdvisorRequestBuilder forHostComponents(Map<String, Set<String>> hostComponents) {
+ public StackAdvisorRequestBuilder forHostComponents(SortedMap<String, SortedSet<String>> hostComponents) {
this.instance.hostComponents = hostComponents;
return this;
}
public StackAdvisorRequestBuilder forHostsGroupBindings(
- Map<String, Set<String>> hostGroupBindings) {
+ SortedMap<String, SortedSet<String>> hostGroupBindings) {
this.instance.hostGroupBindings = hostGroupBindings;
return this;
}
public StackAdvisorRequestBuilder withConfigurations(
- Map<String, Map<String, Map<String, String>>> configurations) {
+ SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations) {
this.instance.configurations = configurations;
return this;
}
@@ -191,13 +206,13 @@ public class StackAdvisorRequest {
}
public StackAdvisorRequestBuilder withUserContext(
- Map<String, String> userContext) {
+ SortedMap<String, String> userContext) {
this.instance.userContext = userContext;
return this;
}
public StackAdvisorRequestBuilder withConfigGroups(
- Set<RecommendationResponse.ConfigGroup> configGroups) {
+ SortedSet<RecommendationResponse.ConfigGroup> configGroups) {
this.instance.configGroups = configGroups;
return this;
}
@@ -213,12 +228,27 @@ public class StackAdvisorRequest {
return this;
}
- public StackAdvisorRequestBuilder withLdapConfig(Map<String, Object> ldapConfig) {
+ public StackAdvisorRequestBuilder withLdapConfig(SortedMap<String, Object> ldapConfig) {
Preconditions.checkNotNull(ldapConfig);
this.instance.ldapConfig = ldapConfig;
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/ConfigurationDependenciesRecommendationCommand.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationDependenciesRecommendationCommand.java
index 209cab9..4d43c80 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationDependenciesRecommendationCommand.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationDependenciesRecommendationCommand.java
@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
@@ -77,7 +78,7 @@ public class ConfigurationDependenciesRecommendationCommand extends
protected Set<HostGroup> processHostGroups(StackAdvisorRequest request) {
Set<HostGroup> resultSet = new HashSet<>();
- for (Map.Entry<String, Set<String>> componentHost : request.getHostComponents().entrySet()) {
+ for (Map.Entry<String, SortedSet<String>> componentHost : request.getHostComponents().entrySet()) {
String hostGroupName = componentHost.getKey();
Set<String> components = componentHost.getValue();
if (hostGroupName != null && components != null) {
@@ -98,7 +99,7 @@ public class ConfigurationDependenciesRecommendationCommand extends
private Set<BindingHostGroup> processHostGroupBindings(StackAdvisorRequest request) {
Set<BindingHostGroup> resultSet = new HashSet<>();
- for (Map.Entry<String, Set<String>> hostBinding : request.getHostGroupBindings().entrySet()) {
+ for (Map.Entry<String, SortedSet<String>> hostBinding : request.getHostGroupBindings().entrySet()) {
String hostGroupName = hostBinding.getKey();
Set<String> hosts = hostBinding.getValue();
if (hostGroupName != null && hosts != null) {
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 15a7240..76f5fa7 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
@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
@@ -34,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
@@ -56,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;
}
@@ -82,7 +86,7 @@ public class ConfigurationRecommendationCommand extends StackAdvisorCommand<Reco
protected Set<HostGroup> processHostGroups(StackAdvisorRequest request) {
Set<HostGroup> resultSet = new HashSet<>();
- for (Map.Entry<String, Set<String>> componentHost : request.getHostComponents().entrySet()) {
+ for (Map.Entry<String, SortedSet<String>> componentHost : request.getHostComponents().entrySet()) {
String hostGroupName = componentHost.getKey();
Set<String> components = componentHost.getValue();
if (hostGroupName != null && components != null) {
@@ -103,7 +107,7 @@ public class ConfigurationRecommendationCommand extends StackAdvisorCommand<Reco
private Set<BindingHostGroup> processHostGroupBindings(StackAdvisorRequest request) {
Set<BindingHostGroup> resultSet = new HashSet<>();
- for (Map.Entry<String, Set<String>> hostBinding : request.getHostGroupBindings().entrySet()) {
+ for (Map.Entry<String, SortedSet<String>> hostBinding : request.getHostGroupBindings().entrySet()) {
String hostGroupName = hostBinding.getKey();
Set<String> hosts = hostBinding.getValue();
if (hostGroupName != null && hosts != null) {
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 56ddcef..a7d45fa 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
@@ -31,6 +31,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
@@ -58,6 +60,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;
@@ -103,6 +106,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;
@@ -119,7 +124,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];
@@ -133,6 +139,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();
@@ -202,13 +214,13 @@ public abstract class StackAdvisorCommand<T extends StackAdvisorResponse> extend
private void populateConfigurations(ObjectNode root,
StackAdvisorRequest request) {
- Map<String, Map<String, Map<String, String>>> configurations =
+ SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations =
request.getConfigurations();
ObjectNode configurationsNode = root.putObject(CONFIGURATIONS_PROPERTY);
for (String siteName : configurations.keySet()) {
ObjectNode siteNode = configurationsNode.putObject(siteName);
- Map<String, Map<String, String>> siteMap = configurations.get(siteName);
+ SortedMap<String, SortedMap<String, String>> siteMap = configurations.get(siteName);
for (String properties : siteMap.keySet()) {
ObjectNode propertiesNode = siteNode.putObject(properties);
@@ -249,7 +261,7 @@ public abstract class StackAdvisorCommand<T extends StackAdvisorResponse> extend
}
}
- private void populateComponentHostsMap(ObjectNode root, Map<String, Set<String>> componentHostsMap) {
+ private void populateComponentHostsMap(ObjectNode root, SortedMap<String, SortedSet<String>> componentHostsMap) {
ArrayNode services = (ArrayNode) root.get(SERVICES_PROPERTY);
Iterator<JsonNode> servicesIter = services.getElements();
@@ -378,22 +390,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());
@@ -410,7 +471,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/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index cffed9e..9b5f74b 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
@@ -115,6 +115,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;
@@ -839,8 +840,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()))
@@ -855,7 +855,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 6f5f4e6..40fe567 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
@@ -914,7 +914,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/KerberosHelperImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
index 23a0d96..008ee47 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
@@ -35,6 +35,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
@@ -684,19 +685,19 @@ public class KerberosHelperImpl implements KerberosHelper {
// This could happen when enabling Kerberos while installing a cluster via Blueprints due to the
// way hosts are discovered during the install process.
if (!hostNames.isEmpty()) {
- Map<String, Map<String, Map<String, String>>> requestConfigurations = new HashMap<>();
+ SortedMap<String, SortedMap<String, SortedMap<String, String>>> requestConfigurations = new TreeMap<>();
if (existingConfigurations != null) {
for (Map.Entry<String, Map<String, String>> configuration : existingConfigurations.entrySet()) {
- Map<String, Map<String, String>> properties = new HashMap<>();
+ SortedMap<String, SortedMap<String, String>> properties = new TreeMap<>();
String configType = configuration.getKey();
- Map<String, String> configurationProperties = configuration.getValue();
+ SortedMap<String, String> configurationProperties = new TreeMap<>(configuration.getValue());
if (configurationProperties == null) {
- configurationProperties = Collections.emptyMap();
+ configurationProperties = Collections.emptySortedMap();
}
if ("cluster-env".equals(configType)) {
- configurationProperties = new HashMap<>(configurationProperties);
+ configurationProperties = new TreeMap<>(configurationProperties);
configurationProperties.put("security_enabled", (kerberosEnabled) ? "true" : "false");
}
@@ -711,18 +712,18 @@ public class KerberosHelperImpl implements KerberosHelper {
Map<String, String> configurationProperties = configuration.getValue();
if ((configurationProperties != null) && !configurationProperties.isEmpty()) {
- Map<String, Map<String, String>> requestConfiguration = requestConfigurations.get(configType);
+ SortedMap<String, SortedMap<String, String>> requestConfiguration = requestConfigurations.get(configType);
if (requestConfiguration == null) {
- requestConfiguration = new HashMap<>();
+ requestConfiguration = new TreeMap<>();
requestConfigurations.put(configType, requestConfiguration);
}
- Map<String, String> requestConfigurationProperties = requestConfiguration.get("properties");
+ SortedMap<String, String> requestConfigurationProperties = requestConfiguration.get("properties");
if (requestConfigurationProperties == null) {
- requestConfigurationProperties = new HashMap<>();
+ requestConfigurationProperties = new TreeMap<>();
} else {
- requestConfigurationProperties = new HashMap<>(requestConfigurationProperties);
+ requestConfigurationProperties = new TreeMap<>(requestConfigurationProperties);
}
requestConfigurationProperties.putAll(configurationProperties);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerStackAdvisorAwareConfigurationHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerStackAdvisorAwareConfigurationHandler.java
index e872de9..e9763ca 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerStackAdvisorAwareConfigurationHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AmbariServerStackAdvisorAwareConfigurationHandler.java
@@ -25,6 +25,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
import java.util.stream.Collectors;
import org.apache.ambari.server.AmbariException;
@@ -185,19 +187,19 @@ class AmbariServerStackAdvisorAwareConfigurationHandler extends AmbariServerConf
* @return a map of services and their configurations
* @throws AmbariException
*/
- private Map<String, Map<String, Map<String, String>>> calculateExistingConfigurations(Cluster cluster) throws AmbariException {
+ private SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculateExistingConfigurations(Cluster cluster) throws AmbariException {
Map<String, Map<String, String>> configurationTags = configHelper.getEffectiveDesiredTags(cluster, null);
Map<String, Map<String, String>> effectiveConfigs = configHelper.getEffectiveConfigProperties(cluster, configurationTags);
- Map<String, Map<String, Map<String, String>>> requestConfigurations = new HashMap<>();
+ SortedMap<String, SortedMap<String, SortedMap<String, String>>> requestConfigurations = new TreeMap<>();
if (effectiveConfigs != null) {
for (Map.Entry<String, Map<String, String>> configuration : effectiveConfigs.entrySet()) {
- Map<String, Map<String, String>> properties = new HashMap<>();
+ SortedMap<String, SortedMap<String, String>> properties = new TreeMap<>();
String configType = configuration.getKey();
- Map<String, String> configurationProperties = configuration.getValue();
+ SortedMap<String, String> configurationProperties = new TreeMap<>(configuration.getValue());
if (configurationProperties == null) {
- configurationProperties = Collections.emptyMap();
+ configurationProperties = Collections.emptySortedMap();
}
properties.put("properties", configurationProperties);
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 a6ed0e5..861a648 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
@@ -815,7 +815,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());
@@ -823,7 +822,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 e1a58ef..d3d672e 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 37215d0..8a8a2eb 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,18 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+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 +51,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 +78,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";
@@ -85,13 +109,18 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
protected static StackAdvisorHelper saHelper;
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 +133,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;
+ SortedMap<String, SortedSet<String>> hgComponentsMap;
+ SortedMap<String, SortedSet<String>> hgHostsMap;
+ SortedMap<String, SortedSet<String>> componentHostsMap;
+ SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations;
+ SortedSet<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");
+ }
}
+ services = (List<String>) servicesObject;
+
+ hgComponentsMap = calculateHostGroupComponentsMap(request);
+ hgHostsMap = calculateHostGroupHostsMap(request);
+ componentHostsMap = calculateComponentHostsMap(hgComponentsMap, hgHostsMap);
+ configurations = calculateConfigurations(request);
+ configGroups = calculateConfigGroups(request);
}
- List<String> 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);
- Map<String, String> userContext = readUserContext(request);
+ SortedMap<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 +220,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)
@@ -170,10 +240,10 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
* @return host-group to components map
*/
@SuppressWarnings("unchecked")
- private Map<String, Set<String>> calculateHostGroupComponentsMap(Request request) {
+ private SortedMap<String, SortedSet<String>> calculateHostGroupComponentsMap(Request request) {
Set<Map<String, Object>> hostGroups = (Set<Map<String, Object>>) getRequestProperty(request,
BLUEPRINT_HOST_GROUPS_PROPERTY);
- Map<String, Set<String>> map = new HashMap<>();
+ SortedMap<String, SortedSet<String>> map = new TreeMap<>();
if (hostGroups != null) {
for (Map<String, Object> hostGroup : hostGroups) {
String hostGroupName = (String) hostGroup.get(BLUEPRINT_HOST_GROUPS_NAME_PROPERTY);
@@ -181,7 +251,7 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
Set<Map<String, Object>> componentsSet = (Set<Map<String, Object>>) hostGroup
.get(BLUEPRINT_HOST_GROUPS_COMPONENTS_PROPERTY);
- Set<String> components = new HashSet<>();
+ SortedSet<String> components = new TreeSet<>();
for (Map<String, Object> component : componentsSet) {
components.add((String) component.get(BLUEPRINT_HOST_GROUPS_COMPONENTS_NAME_PROPERTY));
}
@@ -194,6 +264,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 SortedMap<String, SortedSet<String>> calculateHostGroupComponentsMap(Cluster cluster) {
+ SortedMap<String, SortedSet<String>> map = new TreeMap<>();
+ List<Host> hosts = new ArrayList<>(cluster.getHosts());
+ if (!hosts.isEmpty()) {
+ for (Host host : hosts) {
+ String hostGroupName = host.getHostName();
+
+ SortedSet<String> components = new TreeSet<>();
+ 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.
*
@@ -201,10 +293,10 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
* @return host-group to hosts map
*/
@SuppressWarnings("unchecked")
- private Map<String, Set<String>> calculateHostGroupHostsMap(Request request) {
+ private SortedMap<String, SortedSet<String>> calculateHostGroupHostsMap(Request request) {
Set<Map<String, Object>> bindingHostGroups = (Set<Map<String, Object>>) getRequestProperty(
request, BINDING_HOST_GROUPS_PROPERTY);
- Map<String, Set<String>> map = new HashMap<>();
+ SortedMap<String, SortedSet<String>> map = new TreeMap<>();
if (bindingHostGroups != null) {
for (Map<String, Object> hostGroup : bindingHostGroups) {
String hostGroupName = (String) hostGroup.get(BINDING_HOST_GROUPS_NAME_PROPERTY);
@@ -212,7 +304,7 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
Set<Map<String, Object>> hostsSet = (Set<Map<String, Object>>) hostGroup
.get(BINDING_HOST_GROUPS_HOSTS_PROPERTY);
- Set<String> hosts = new HashSet<>();
+ SortedSet<String> hosts = new TreeSet<>();
for (Map<String, Object> host : hostsSet) {
hosts.add((String) host.get(BINDING_HOST_GROUPS_HOSTS_NAME_PROPERTY));
}
@@ -224,6 +316,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 SortedMap<String, SortedSet<String>> calculateHostGroupHostsMap(Cluster cluster) {
+ SortedMap<String, SortedSet<String>> map = new TreeMap<>();
+
+ List<Host> hosts = new ArrayList<>(cluster.getHosts());
+ if (!hosts.isEmpty()) {
+ for (Host host : hosts) {
+ map.put(host.getHostName(), new TreeSet<String>(){{add(host.getHostName());}});
+ }
+ }
+
+ return map;
+ }
+
protected List<ChangedConfigInfo> calculateChangedConfigurations(Request request) {
List<ChangedConfigInfo> configs =
new LinkedList<>();
@@ -236,10 +346,10 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
return configs;
}
- protected Set<RecommendationResponse.ConfigGroup> calculateConfigGroups(Request request) {
+ protected SortedSet<RecommendationResponse.ConfigGroup> calculateConfigGroups(Request request) {
- Set<RecommendationResponse.ConfigGroup> configGroups =
- new HashSet<>();
+ SortedSet<RecommendationResponse.ConfigGroup> configGroups =
+ new TreeSet<>();
Set<HashMap<String, Object>> configGroupsProperties =
(HashSet<HashMap<String, Object>>) getRequestProperty(request, CONFIG_GROUPS_PROPERTY);
@@ -270,6 +380,40 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
return configGroups;
}
+ protected SortedSet<RecommendationResponse.ConfigGroup> calculateConfigGroups(Cluster cluster, Request request) {
+
+ SortedSet<RecommendationResponse.ConfigGroup> configGroups =
+ new TreeSet<>();
+
+ 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" }
@@ -277,8 +421,8 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
* @param request
* @return
*/
- protected Map<String, String> readUserContext(Request request) {
- HashMap<String, String> userContext = new HashMap<>();
+ protected SortedMap<String, String> readUserContext(Request request) {
+ SortedMap<String, String> userContext = new TreeMap<>();
if (null != getRequestProperty(request, USER_CONTEXT_OPERATION_PROPERTY)) {
userContext.put(OPERATION_PROPERTY,
(String) getRequestProperty(request, USER_CONTEXT_OPERATION_PROPERTY));
@@ -292,8 +436,8 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
protected static final String CONFIGURATIONS_PROPERTY_ID = "recommendations/blueprint/configurations/";
- protected Map<String, Map<String, Map<String, String>>> calculateConfigurations(Request request) {
- Map<String, Map<String, Map<String, String>>> configurations = new HashMap<>();
+ protected SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculateConfigurations(Request request) {
+ SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations = new TreeMap<>();
Map<String, Object> properties = request.getProperties().iterator().next();
for (String property : properties.keySet()) {
if (property.startsWith(CONFIGURATIONS_PROPERTY_ID)) {
@@ -304,15 +448,15 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
String propertiesProperty = propertyPath[1];
String propertyName = propertyPath[2];
- Map<String, Map<String, String>> siteMap = configurations.get(siteName);
+ SortedMap<String, SortedMap<String, String>> siteMap = configurations.get(siteName);
if (siteMap == null) {
- siteMap = new HashMap<>();
+ siteMap = new TreeMap<>();
configurations.put(siteName, siteMap);
}
- Map<String, String> propertiesMap = siteMap.get(propertiesProperty);
+ SortedMap<String, String> propertiesMap = siteMap.get(propertiesProperty);
if (propertiesMap == null) {
- propertiesMap = new HashMap<>();
+ propertiesMap = new TreeMap<>();
siteMap.put(propertiesProperty, propertiesMap);
}
@@ -331,25 +475,50 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
return configurations;
}
+ protected SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculateConfigurations(Cluster cluster, String serviceName)
+ throws AmbariException {
+ SortedMap<String, SortedMap<String, SortedMap<String, String>>> configurations = new TreeMap<>();
+ 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(),
+ new TreeMap<String, SortedMap<String, String>>(){{put("properties", new TreeMap<>(config.getProperties()));}});
+ }
+ return configurations;
+ }
+
@SuppressWarnings("unchecked")
- private Map<String, Set<String>> calculateComponentHostsMap(Map<String, Set<String>> hostGroups,
- Map<String, Set<String>> bindingHostGroups) {
+ private SortedMap<String, SortedSet<String>> calculateComponentHostsMap(SortedMap<String, SortedSet<String>> hostGroups,
+ SortedMap<String, SortedSet<String>> bindingHostGroups) {
/*
* ClassCastException may occur in case of body inconsistency: property
* missed, etc.
*/
- Map<String, Set<String>> componentHostsMap = new HashMap<>();
+ SortedMap<String, SortedSet<String>> componentHostsMap = new TreeMap<>();
if (null != bindingHostGroups && null != hostGroups) {
- for (Map.Entry<String, Set<String>> hgComponents : hostGroups.entrySet()) {
+ for (Map.Entry<String, SortedSet<String>> hgComponents : hostGroups.entrySet()) {
String hgName = hgComponents.getKey();
Set<String> components = hgComponents.getValue();
Set<String> hosts = bindingHostGroups.get(hgName);
for (String component : components) {
- Set<String> componentHosts = componentHostsMap.get(component);
+ SortedSet<String> componentHosts = componentHostsMap.get(component);
if (componentHosts == null) { // if was not initialized
- componentHosts = new HashSet<>();
+ componentHosts = new TreeSet<>();
componentHostsMap.put(component, componentHosts);
}
componentHosts.addAll(hosts);
@@ -360,6 +529,23 @@ public abstract class StackAdvisorResourceProvider extends ReadOnlyResourceProvi
return componentHostsMap;
}
+ @SuppressWarnings("unchecked")
+ private SortedMap<String, SortedSet<String>> calculateComponentHostsMap(Cluster cluster) {
+ /*
+ * ClassCastException may occur in case of body inconsistency: property
+ * missed, etc.
+ */
+
+ SortedMap<String, SortedSet<String>> componentHostsMap = new TreeMap<>();
+ List<ServiceComponentHost> schs = cluster.getServiceComponentHosts();
+ for (ServiceComponentHost sch : schs) {
+ componentHostsMap.putIfAbsent(sch.getServiceComponentName(), new TreeSet<>());
+ 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 a150796..817563d 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
@@ -24,6 +24,8 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.controller.ClusterResponse;
@@ -109,7 +111,7 @@ public interface Cluster {
* @param serviceNames
* @return a map of (filtered) components to hosts
*/
- Map<String, Set<String>> getServiceComponentHostMap(Set<String> hostNames, Set<String> serviceNames);
+ SortedMap<String, SortedSet<String>> getServiceComponentHostMap(Set<String> hostNames, Set<String> serviceNames);
/**
* Get all ServiceComponentHosts for a given service and optional component
@@ -514,6 +516,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/cluster/ClusterImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index 4ca2caf..06a6067 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,10 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+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 +535,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,8 +809,8 @@ 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<>();
+ public SortedMap<String, SortedSet<String>> getServiceComponentHostMap(Set<String> hostNames, Set<String> serviceNames) {
+ SortedMap<String, SortedSet<String>> componentHostMap = new TreeMap<>();
Collection<Host> hosts = getHosts();
@@ -818,10 +827,10 @@ public class ClusterImpl implements Cluster {
// If the service for this ServiceComponentHost is not filtered out, continue processing
if ((serviceNames == null) || serviceNames.contains(sch.getServiceName())) {
String component = sch.getServiceComponentName();
- Set<String> componentHosts = componentHostMap.get(component);
+ SortedSet<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/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 8f85216..d7c0ae9 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();
@@ -201,7 +220,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();
@@ -221,7 +240,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();
@@ -241,7 +260,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();
@@ -251,6 +270,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);
@@ -260,7 +397,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();
@@ -273,6 +410,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..7d943d6 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
@@ -29,6 +29,10 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
@@ -44,16 +48,19 @@ 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<>();
- Set<String> components1 = new HashSet<>();
+ SortedMap<String, SortedSet<String>> componentHostGroupMap = new TreeMap<>();
+ SortedSet<String> components1 = new TreeSet<>();
components1.add("component1");
components1.add("component4");
components1.add("component5");
componentHostGroupMap.put("group1", components1);
- Set<String> components2 = new HashSet<>();
+ SortedSet<String> components2 = new TreeSet<>();
components2.add("component2");
components2.add("component3");
componentHostGroupMap.put("group2", components2);
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 5d7339a..d110023 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
@@ -43,6 +43,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;
@@ -80,6 +81,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;
@@ -103,7 +107,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();
@@ -123,7 +127,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();
@@ -151,7 +155,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();
@@ -180,7 +184,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();
@@ -215,8 +220,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\"}}");
@@ -244,7 +249,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\"}}");
@@ -266,7 +271,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\"}}");
@@ -294,7 +299,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);
@@ -310,7 +315,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);
@@ -318,6 +323,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);
}
@@ -345,8 +418,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 9721e8b..a056c1b 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,10 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import javax.persistence.EntityManager;
@@ -1994,7 +1998,7 @@ public class KerberosHelperTest extends EasyMockSupport {
serviceNames.add("EXISTING_SERVICE");
serviceNames.add("PRECONFIGURE_SERVICE");
- Map<String, Set<String>> hostMap = new HashMap<>();
+ SortedMap<String, SortedSet<String>> hostMap = new TreeMap<>();
Map<String, Service> services = new HashMap<>();
@@ -2377,12 +2381,12 @@ public class KerberosHelperTest extends EasyMockSupport {
services.put("SERVICE2", service2);
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"));
+ SortedMap<String, SortedSet<String>> serviceComponentHostMap = new TreeMap<>();
+ 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/AmbariServerSSOConfigurationHandlerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AmbariServerSSOConfigurationHandlerTest.java
index 9c93cd3..861c176 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AmbariServerSSOConfigurationHandlerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AmbariServerSSOConfigurationHandlerTest.java
@@ -30,7 +30,10 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
@@ -102,7 +105,8 @@ public class AmbariServerSSOConfigurationHandlerTest extends EasyMockSupport {
StackId stackId = new StackId("HDP-3.0");
- Map<String, Set<String>> serviceComponentHostMap = Collections.singletonMap("ATLAS_COMPONENT", Collections.singleton("host1"));
+ SortedMap<String, SortedSet<String>> serviceComponentHostMap =
+ new TreeMap<String, SortedSet<String>>(){{put("ATLAS_COMPONENT", new TreeSet<String>(){{add("host1");}});}};
Host host = createMock(Host.class);
expect(host.getHostName()).andReturn("host1").once();
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..844b2ae 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,180 @@
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, true);
+
+ 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, false);
+
+ 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,
+ Boolean configsOnlyResponse) 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(configsOnlyResponse).
+ 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/StackAdvisorResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProviderTest.java
index a6c7d42..61d99c7 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/StackAdvisorResourceProviderTest.java
@@ -34,6 +34,7 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
import javax.annotation.Nonnull;
@@ -55,11 +56,11 @@ public class StackAdvisorResourceProviderTest {
CONFIGURATIONS_PROPERTY_ID + "site/properties/string_prop", "string",
CONFIGURATIONS_PROPERTY_ID + "site/properties/array_prop", Lists.newArrayList("array1", "array2"));
- Map<String, Map<String, Map<String, String>>> calculatedConfigurations = provider.calculateConfigurations(request);
+ SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculatedConfigurations = provider.calculateConfigurations(request);
assertNotNull(calculatedConfigurations);
assertEquals(1, calculatedConfigurations.size());
- Map<String, Map<String, String>> site = calculatedConfigurations.get("site");
+ SortedMap<String, SortedMap<String, String>> site = calculatedConfigurations.get("site");
assertNotNull(site);
assertEquals(1, site.size());
Map<String, String> properties = site.get("properties");
@@ -112,11 +113,11 @@ public class StackAdvisorResourceProviderTest {
CONFIGURATIONS_PROPERTY_ID + "site/properties/string_prop", null,
CONFIGURATIONS_PROPERTY_ID + "site/properties/array_prop", Lists.newArrayList("array1", "array2"));
- Map<String, Map<String, Map<String, String>>> calculatedConfigurations = provider.calculateConfigurations(request);
+ SortedMap<String, SortedMap<String, SortedMap<String, String>>> calculatedConfigurations = provider.calculateConfigurations(request);
assertNotNull(calculatedConfigurations);
assertEquals(1, calculatedConfigurations.size());
- Map<String, Map<String, String>> site = calculatedConfigurations.get("site");
+ SortedMap<String, SortedMap<String, String>> site = calculatedConfigurations.get("site");
assertNotNull(site);
assertEquals(1, site.size());
Map<String, String> properties = site.get("properties");
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 1eba8fa..5c0eb4f 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
@@ -29,6 +29,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.StackAdvisorResponse.Version;
@@ -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-server/src/test/java/org/apache/ambari/server/controller/metrics/RestMetricsPropertyProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/RestMetricsPropertyProviderTest.java
index 6bcdc08..8370772 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/RestMetricsPropertyProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/metrics/RestMetricsPropertyProviderTest.java
@@ -490,30 +490,35 @@ public class RestMetricsPropertyProviderTest {
RestMetricsPropertyProvider restMetricsPropertyProvider = createRestMetricsPropertyProvider(metricDefinition, componentMetrics, streamProvider,
metricsHostProvider);
- // set the provider timeout to 50 millis
- restMetricsPropertyProvider.setPopulateTimeout(50L);
-
- Resource resource = new ResourceImpl(Resource.Type.HostComponent);
+ // set the provider timeout to -1 millis to guarantee timeout
+ restMetricsPropertyProvider.setPopulateTimeout(-1L);
+ try {
+ Resource resource = new ResourceImpl(Resource.Type.HostComponent);
- resource.setProperty("HostRoles/cluster_name", "c1");
- resource.setProperty(HOST_COMPONENT_HOST_NAME_PROPERTY_ID, "domu-12-31-39-0e-34-e1.compute-1.internal");
- resource.setProperty(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID, "STORM_REST_API");
+ resource.setProperty("HostRoles/cluster_name", "c1");
+ resource.setProperty(HOST_COMPONENT_HOST_NAME_PROPERTY_ID, "domu-12-31-39-0e-34-e1.compute-1.internal");
+ resource.setProperty(HOST_COMPONENT_COMPONENT_NAME_PROPERTY_ID, "STORM_REST_API");
- resources.add(resource);
+ resources.add(resource);
- // request with an empty set should get all supported properties
- Request request = PropertyHelper.getReadRequest(Collections.emptySet());
+ // request with an empty set should get all supported properties
+ Request request = PropertyHelper.getReadRequest(Collections.emptySet());
- Set<Resource> resourceSet = restMetricsPropertyProvider.populateResources(resources, request, null);
+ Set<Resource> resourceSet = restMetricsPropertyProvider.populateResources(resources, request, null);
- // make sure that the thread running the stream provider has completed
- Thread.sleep(150L);
+ // make sure that the thread running the stream provider has completed
+ Thread.sleep(150L);
- Assert.assertEquals(0, resourceSet.size());
+ Assert.assertEquals(0, resourceSet.size());
- // assert that properties never get set on the resource
- Assert.assertNull(resource.getPropertyValue("metrics/api/cluster/summary/tasks.total"));
- Assert.assertNull(resource.getPropertyValue("metrics/api/cluster/summary/supervisors"));
+ // assert that properties never get set on the resource
+ Assert.assertNull(resource.getPropertyValue("metrics/api/cluster/summary/tasks.total"));
+ Assert.assertNull(resource.getPropertyValue("metrics/api/cluster/summary/supervisors"));
+ } finally {
+ // reset default value
+ restMetricsPropertyProvider.setPopulateTimeout(injector.getInstance(Configuration.class)
+ .getPropertyProvidersCompletionServiceTimeout());
+ }
}
public static class TestMetricsHostProvider implements MetricHostProvider {
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
index da75ed2..655b703 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
@@ -42,6 +42,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
import javax.persistence.EntityManager;
@@ -747,7 +749,7 @@ public class ClusterTest {
ServiceComponentHost scDNH2 = serviceComponentHostFactory.createNew(scDN, "h2");
scDN.addServiceComponentHost(scDNH2);
- Map<String, Set<String>> componentHostMap;
+ SortedMap<String, SortedSet<String>> componentHostMap;
componentHostMap = c1.getServiceComponentHostMap(null, null);
Assert.assertEquals(2, componentHostMap.size());
@@ -789,7 +791,7 @@ public class ClusterTest {
ServiceComponentHost schJTH1 = serviceComponentHostFactory.createNew(scJT, "h1");
scJT.addServiceComponentHost(schJTH1);
- Map<String, Set<String>> componentHostMap;
+ SortedMap<String, SortedSet<String>> componentHostMap;
componentHostMap = c1.getServiceComponentHostMap(null, Collections.singleton("HDFS"));
Assert.assertEquals(2, componentHostMap.size());
@@ -847,7 +849,7 @@ public class ClusterTest {
ServiceComponentHost schJTH1 = serviceComponentHostFactory.createNew(scJT, "h1");
scJT.addServiceComponentHost(schJTH1);
- Map<String, Set<String>> componentHostMap;
+ SortedMap<String, SortedSet<String>> componentHostMap;
componentHostMap = c1.getServiceComponentHostMap(Collections.singleton("h1"), null);
Assert.assertEquals(3, componentHostMap.size());
@@ -906,7 +908,7 @@ public class ClusterTest {
ServiceComponentHost schJTH1 = serviceComponentHostFactory.createNew(scJT, "h1");
scJT.addServiceComponentHost(schJTH1);
- Map<String, Set<String>> componentHostMap;
+ SortedMap<String, SortedSet<String>> componentHostMap;
componentHostMap = c1.getServiceComponentHostMap(Collections.singleton("h1"), Collections.singleton("HDFS"));
Assert.assertEquals(2, componentHostMap.size());
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);
});
});
});