From 03981fa014f936979eac2a066282fcb50e4fcf6d Mon Sep 17 00:00:00 2001 From: Scott Duan Date: Tue, 6 Feb 2018 17:29:35 -0800 Subject: [PATCH 1/3] A new REST API: GET /api/v1/hosts?format=summary or /api/v1/clusters/{cluster_name}/hosts?format=summary is supported with this implementation and return the number of hosts running each possible operating system. More aggregation info of hosts can be also added in this code structure. --- .../query/render/HostInfoSummaryRenderer.java | 95 ++++++++++++ .../HostInfoSummaryResourceDefinition.java | 65 +++++++++ .../ResourceInstanceFactoryImpl.java | 4 + .../server/api/services/HostService.java | 24 +-- .../internal/DefaultProviderModule.java | 4 + .../HostInfoSummaryResourceProvider.java | 123 ++++++++++++++++ .../server/controller/spi/Resource.java | 4 +- .../server/orm/dao/HostInfoSummaryDAO.java | 75 ++++++++++ .../server/orm/dao/HostInfoSummaryDTO.java | 37 +++++ .../entities/ClusterHostMappingEntity.java | 75 ++++++++++ .../entities/ClusterHostMappingEntityPK.java | 70 +++++++++ .../server/orm/models/HostInfoSummary.java | 60 ++++++++ .../main/resources/META-INF/persistence.xml | 1 + .../server/api/services/HostServiceTest.java | 19 ++- .../HostInfoSummaryResourceProviderTest.java | 138 ++++++++++++++++++ 15 files changed, 779 insertions(+), 15 deletions(-) create mode 100644 ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java create mode 100644 ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java create mode 100644 ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java create mode 100644 ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDAO.java create mode 100644 ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDTO.java create mode 100644 ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntity.java create mode 100644 ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntityPK.java create mode 100644 ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java create mode 100644 ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java new file mode 100644 index 00000000000..aae1f801f22 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java @@ -0,0 +1,95 @@ +/* + * 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.api.query.render; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.ambari.server.api.query.QueryInfo; +import org.apache.ambari.server.api.services.Request; +import org.apache.ambari.server.api.services.Result; +import org.apache.ambari.server.api.services.ResultImpl; +import org.apache.ambari.server.api.services.ResultPostProcessor; +import org.apache.ambari.server.api.services.ResultPostProcessorImpl; +import org.apache.ambari.server.api.util.TreeNode; +import org.apache.ambari.server.api.util.TreeNodeImpl; +import org.apache.ambari.server.controller.internal.HostInfoSummaryResourceProvider; +import org.apache.ambari.server.controller.internal.ResourceImpl; +import org.apache.ambari.server.controller.spi.Resource; +import org.apache.commons.lang.StringUtils; + +public class HostInfoSummaryRenderer extends BaseRenderer implements Renderer { + + /** + * {@inheritDoc} + */ + @Override + public TreeNode> finalizeProperties( + TreeNode queryTree, boolean isCollection) { + + QueryInfo queryInfo = queryTree.getObject(); + TreeNode> resultTree = new TreeNodeImpl<>( + null, queryInfo.getProperties(), queryTree.getName()); + + copyPropertiesToResult(queryTree, resultTree); + + return resultTree; + } + + @Override + public boolean requiresPropertyProviderInput() { + return false; + } + + @Override + public Result finalizeResult(Result queryResult) { + // Convert fully qualified properties into short property names and flat the queryResult datastructure + Map summaryMap = new HashMap<>(); + TreeNode resultTree = queryResult.getResultTree(); + Collection> nodes = resultTree.getChildren(); + if (nodes != null && !nodes.isEmpty()) { + Resource resource = (Resource)((TreeNode)nodes.iterator().next()).getObject(); + Object o = resource.getPropertyValue(HostInfoSummaryResourceProvider.CLUSTER_NAME); + if (o != null && o instanceof String && StringUtils.isNotBlank((String)o)) { + summaryMap.put("cluster_name", (String)o); + } + o = resource.getPropertiesMap().get(HostInfoSummaryResourceProvider.HOSTS_SUMMARY); + if (o != null && o instanceof Map && ((Map)o).size() > 0) { + summaryMap.putAll((Map)o); + } + } + + Resource resultResource = new ResourceImpl(Resource.Type.HostSummary); + resultResource.setProperty("hosts_summary", summaryMap); + Result summaryResult = new ResultImpl(true); + TreeNode summaryTree = summaryResult.getResultTree(); + summaryTree.addChild(resultResource, "hosts_summary"); + + return summaryResult; + } + + @Override + public ResultPostProcessor getResultPostProcessor(Request request) { + // simply return the native rendering + return new ResultPostProcessorImpl(request); + } +} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java new file mode 100644 index 00000000000..74b69bc8c94 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java @@ -0,0 +1,65 @@ +/* + * 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.api.resources; + +import java.util.Collection; +import java.util.Collections; + +import org.apache.ambari.server.api.query.render.HostInfoSummaryRenderer; +import org.apache.ambari.server.api.query.render.Renderer; +import org.apache.ambari.server.controller.spi.Request; +import org.apache.ambari.server.controller.spi.Resource; + +public class HostInfoSummaryResourceDefinition extends BaseResourceDefinition { + /** + * Constructor. + */ + public HostInfoSummaryResourceDefinition() { + super(Resource.Type.HostSummary); + } + + /** + * {@inheritDoc} + */ + @Override + public String getPluralName() { + return "hostInfoSummary"; + } + + /** + * {@inheritDoc} + */ + @Override + public String getSingularName() { + return "hostInfoSummary"; + } + + @Override + public Collection getDeleteDirectives() { + return Collections.singleton(Request.DIRECTIVE_DRY_RUN); + } + + /** + * {@inheritDoc} + */ + @Override + public Renderer getRenderer(String name) { + return new HostInfoSummaryRenderer(); + } +} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java index 7d16105256b..e4d2b094028 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java @@ -535,6 +535,10 @@ public static ResourceDefinition getResourceDefinition(Resource.Type type, Map mapIds = new HashMap<>(); mapIds.put(Resource.Type.Host, hostName); if (clusterName != null) { mapIds.put(Resource.Type.Cluster, clusterName); } - return createResource(Resource.Type.Host, mapIds); + return createResource(type, mapIds); } } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java index 1dbe9032f6d..52803bc62e4 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java @@ -132,6 +132,10 @@ protected ResourceProvider createResourceProvider(Resource.Type type) { return new ArtifactResourceProvider(managementController); case RemoteCluster: return new RemoteClusterResourceProvider(); + case Host: + return new HostResourceProvider(managementController); + case HostSummary: + return new HostInfoSummaryResourceProvider(managementController); default: LOGGER.debug("Delegating creation of resource provider for: {} to the AbstractControllerResourceProvider", type.getInternalType()); return AbstractControllerResourceProvider.getResourceProvider(type, managementController); diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java new file mode 100644 index 00000000000..fec56a5100f --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.controller.internal; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + + +import org.apache.ambari.server.controller.AmbariManagementController; +import org.apache.ambari.server.controller.spi.ExtendedResourceProvider; +import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; +import org.apache.ambari.server.controller.spi.NoSuchResourceException; +import org.apache.ambari.server.controller.spi.Predicate; +import org.apache.ambari.server.controller.spi.QueryResponse; +import org.apache.ambari.server.controller.spi.Request; +import org.apache.ambari.server.controller.spi.Resource; +import org.apache.ambari.server.controller.spi.SystemException; +import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; +import org.apache.ambari.server.orm.models.HostInfoSummary; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + + +public class HostInfoSummaryResourceProvider extends ReadOnlyResourceProvider implements ExtendedResourceProvider { + + private static final Logger LOG = LoggerFactory.getLogger(HostInfoSummaryResourceProvider.class); + + public static final String HOSTS_SUMMARY = "HostsSummary/hosts_summary"; + public static final String CLUSTER_NAME = "HostsSummary/cluster_name"; + + private static final Set PROPERTY_IDS = new HashSet<>(); + + private static final Map KEY_PROPERTY_IDS = new HashMap<>(); + + static { + PROPERTY_IDS.add(CLUSTER_NAME); + PROPERTY_IDS.add(HOSTS_SUMMARY); + KEY_PROPERTY_IDS.put(Resource.Type.Cluster, CLUSTER_NAME); + KEY_PROPERTY_IDS.put(Resource.Type.HostSummary, HOSTS_SUMMARY); + } + + /** + * Constructor. + * + * @param controller + */ + HostInfoSummaryResourceProvider(AmbariManagementController controller) { + super(Resource.Type.HostSummary, PROPERTY_IDS, KEY_PROPERTY_IDS, controller); + } + + /** + * {@inheritDoc} + */ + @Override + public QueryResponse queryForResources(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, NoSuchResourceException, + NoSuchParentResourceException { + + return new QueryResponseImpl(getResources(request, predicate)); + } + + /** + * {@inheritDoc} + */ + @Override + public Set getResources(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, + NoSuchResourceException, NoSuchParentResourceException { + + Set requestPropertyIds = getRequestPropertyIds(request, predicate); + + // use a collection which preserves order since JPA sorts the results + Set results = new LinkedHashSet<>(); + + HostInfoSummary hostInfoSummary = new HostInfoSummary(); + Resource resource = new ResourceImpl(Resource.Type.HostSummary); + // predicate may or may not be there depending on how to query + // if it has cluster name, the host summary is cluster scope + // otherwise, the host summary covers all the hosts cross the clusters + Set> propertyMap = getPropertyMaps(predicate); + String clusterName = null; + if (propertyMap.size() != 0) { + for (Map property : propertyMap) { + clusterName = (String) property.get(CLUSTER_NAME); + if (StringUtils.isNotBlank(clusterName)) { + setResourceProperty(resource, CLUSTER_NAME, clusterName, requestPropertyIds); + break; + } + } + } + hostInfoSummary = hostInfoSummary.getHostInfoSummary(clusterName); + setResourceProperty(resource, HOSTS_SUMMARY, hostInfoSummary.getSummary(), requestPropertyIds); + results.add(resource); + + return results; + } + + + @Override + protected Set getPKPropertyIds() { + return new HashSet(); + } +} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java index 1524d9aff27..c6fce600577 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java @@ -175,7 +175,8 @@ enum InternalType { VersionDefinition, ClusterKerberosDescriptor, LoggingQuery, - RemoteCluster; + RemoteCluster, + HostSummary; /** * Get the {@link Type} that corresponds to this InternalType. @@ -312,6 +313,7 @@ final class Type implements Comparable{ public static final Type ClusterKerberosDescriptor = InternalType.ClusterKerberosDescriptor.getType(); public static final Type LoggingQuery = InternalType.LoggingQuery.getType(); public static final Type RemoteCluster = InternalType.RemoteCluster.getType(); + public static final Type HostSummary = InternalType.HostSummary.getType(); /** diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDAO.java new file mode 100644 index 00000000000..22bd9423947 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDAO.java @@ -0,0 +1,75 @@ +/* + * 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.orm.dao; + +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; + +import org.apache.ambari.server.orm.RequiresSession; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; + +@Singleton +public class HostInfoSummaryDAO { + + private static final Logger LOG = LoggerFactory.getLogger(HostInfoSummaryDAO.class); + + // Whenever we want to aggregate more host info in summary, we need to update these two statements + // also we must update HostInfoSummary.java + private static final String SUMMARY_WITH_CLUSTER_NAME_QUERY_STATEMENT = + "SELECT NEW %s(" + + "host.osType, COUNT(host.osType)) " + + "FROM ClusterHostMappingEntity clusters " + + "JOIN ClusterEntity cluster ON clusters.clusterId = cluster.clusterId " + + "JOIN HostEntity host ON clusters.hostId = host.hostId " + + "WHERE cluster.clusterName = :cluster_name " + + "GROUP BY host.osType"; + + private static final String SUMMARY_WITHOUT_CLUSTER_NAME_QUERY_STATEMENT = + "SELECT NEW %s(" + + "host.osType, COUNT(host.osType)) " + + "FROM ClusterHostMappingEntity clusters " + + "JOIN HostEntity host ON clusters.hostId = host.hostId " + + "GROUP BY host.osType"; + + @Inject + private Provider entityManagerProvider; + + @Inject + private DaoUtils daoUtils; + + @RequiresSession + public List findHostInfoSummary(String clusterName) { + String sql = String.format(StringUtils.isBlank(clusterName) ? SUMMARY_WITHOUT_CLUSTER_NAME_QUERY_STATEMENT : SUMMARY_WITH_CLUSTER_NAME_QUERY_STATEMENT, HostInfoSummaryDTO.class.getName()); + + TypedQuery query = entityManagerProvider.get().createQuery(sql, HostInfoSummaryDTO.class); + if (StringUtils.isNotBlank(clusterName)) { + query.setParameter("cluster_name", clusterName); + } + return daoUtils.selectList(query); + } + +} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDTO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDTO.java new file mode 100644 index 00000000000..19b60e1746b --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDTO.java @@ -0,0 +1,37 @@ +/* + * 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.orm.dao; + +public class HostInfoSummaryDTO { + + private String os_type; + private int os_type_count = 0; + + public HostInfoSummaryDTO(String os_type, Number os_type_count) { + this.os_type = os_type; + this.os_type_count = null == os_type_count ? 0 : os_type_count.intValue(); + } + + public String getOsType() { + return os_type; + } + + public int getOsTypeCount() { + return os_type_count; + } +} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntity.java new file mode 100644 index 00000000000..a6d5deb22ce --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntity.java @@ -0,0 +1,75 @@ +/* + * 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.orm.entities; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Table; + +@Entity +@Table(name = "ClusterHostMapping") +@IdClass(ClusterHostMappingEntityPK.class) +public class ClusterHostMappingEntity { + + @Id + @Column(name = "cluster_id", nullable = false, insertable = true, updatable = true) + private Long clusterId; + + @Id + @Column(name = "host_id", nullable = false, insertable = true, updatable = false) + private Long hostId; + + public Long getClusterId() { + return clusterId; + } + + public void setClusterId(Long clusterId) { + this.clusterId = clusterId; + } + + public Long getHostId() { + return hostId; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ClusterHostMappingEntity that = (ClusterHostMappingEntity) o; + + if (!clusterId.equals(that.clusterId)) return false; + if (!hostId.equals(that.hostId)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = clusterId.hashCode(); + result = 31 * result + hostId.hashCode(); + return result; + } +} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntityPK.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntityPK.java new file mode 100644 index 00000000000..9729b76b94a --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntityPK.java @@ -0,0 +1,70 @@ +/* + * 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.orm.entities; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Id; + +public class ClusterHostMappingEntityPK implements Serializable { + + private Long clusterId; + private Long hostId; + + @Id + @Column(name = "cluster_id", nullable = false, insertable = true, updatable = true) + public Long getClusterId() { + return clusterId; + } + + public void setClusterId(Long clusterId) { + this.clusterId = clusterId; + } + + @Id + @Column(name = "host_id", nullable = false, insertable = true, updatable = true) + public Long getHostId() { + return hostId; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ClusterHostMappingEntityPK that = (ClusterHostMappingEntityPK) o; + + if (!clusterId.equals(that.clusterId)) return false; + if (!hostId.equals(that.hostId)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = clusterId.hashCode(); + result = 31 * result + hostId.hashCode(); + return result; + } +} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java new file mode 100644 index 00000000000..4ead73d990c --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ambari.server.orm.models; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.ambari.server.StaticallyInject; +import org.apache.ambari.server.orm.dao.HostInfoSummaryDAO; +import org.apache.ambari.server.orm.dao.HostInfoSummaryDTO; + +import com.google.inject.Inject; + +@StaticallyInject +public class HostInfoSummary { + + static public String HOST_INFO_SUMMARY_OS = "operating_systems"; + + @Inject + private static HostInfoSummaryDAO hostInfoSummaryDAO; + + private Map summary = new HashMap(); + + public HostInfoSummary getHostInfoSummary(String cluster_name) { + + List summaryDTOS = hostInfoSummaryDAO.findHostInfoSummary(cluster_name); + List> osSummaryList = new ArrayList<>(); + for (HostInfoSummaryDTO summaryDTO : summaryDTOS) { + osSummaryList.add(Stream.of(new AbstractMap.SimpleImmutableEntry<>(summaryDTO.getOsType(), summaryDTO.getOsTypeCount())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); + } + summary.put(HOST_INFO_SUMMARY_OS, osSummaryList); + return this; + } + + public Map getSummary() { + return summary; + } + +} diff --git a/ambari-server/src/main/resources/META-INF/persistence.xml b/ambari-server/src/main/resources/META-INF/persistence.xml index 6f930b1440a..abe1d7c519d 100644 --- a/ambari-server/src/main/resources/META-INF/persistence.xml +++ b/ambari-server/src/main/resources/META-INF/persistence.xml @@ -35,6 +35,7 @@ org.apache.ambari.server.orm.entities.ClusterSettingEntity org.apache.ambari.server.orm.entities.ClusterServiceEntity org.apache.ambari.server.orm.entities.ClusterStateEntity + org.apache.ambari.server.orm.entities.ClusterHostMappingEntity org.apache.ambari.server.orm.entities.ConfigGroupConfigMappingEntity org.apache.ambari.server.orm.entities.ConfigGroupEntity org.apache.ambari.server.orm.entities.ConfigGroupHostMappingEntity diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java index 33eb12ceef8..042fad4a0d0 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java @@ -31,6 +31,7 @@ import org.apache.ambari.server.api.resources.ResourceInstance; import org.apache.ambari.server.api.services.parsers.RequestBodyParser; import org.apache.ambari.server.api.services.serializers.ResultSerializer; +import org.apache.ambari.server.controller.spi.Resource; /** * Unit tests for HostService. @@ -48,8 +49,20 @@ public List getTestInvocations() throws Exception { //getHosts service = new TestHostService("clusterName", null); - m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class); - args = new Object[] {null, getHttpHeaders(), getUriInfo()}; + m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class, String.class); + args = new Object[] {null, getHttpHeaders(), getUriInfo(), null}; + listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null)); + + //getHostsSummary + service = new TestHostService("clusterName", null); + m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class, String.class); + args = new Object[] {null, getHttpHeaders(), getUriInfo(), "summary"}; + listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null)); + + //getHostsSummary + service = new TestHostService(null, null); + m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class, String.class); + args = new Object[] {null, getHttpHeaders(), getUriInfo(), "summary"}; listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null)); //createHost @@ -96,7 +109,7 @@ private TestHostService(String clusterId, String hostId) { } @Override - protected ResourceInstance createHostResource(String clusterName, String hostName) { + protected ResourceInstance createHostResource(String clusterName, String hostName, Resource.Type type) { assertEquals(m_clusterId, clusterName); assertEquals(m_hostId, hostName); return getTestResource(); diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java new file mode 100644 index 00000000000..d8b26c703a9 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ambari.server.controller.internal; + +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.Response; + +import org.apache.ambari.server.api.handlers.ReadHandler; +import org.apache.ambari.server.api.handlers.RequestHandler; +import org.apache.ambari.server.api.predicate.PredicateCompiler; +import org.apache.ambari.server.api.query.render.HostInfoSummaryRenderer; +import org.apache.ambari.server.api.resources.ResourceInstance; +import org.apache.ambari.server.api.resources.ResourceInstanceFactory; +import org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl; +import org.apache.ambari.server.api.services.GetRequest; +import org.apache.ambari.server.api.services.Result; +import org.apache.ambari.server.api.services.ResultStatus; +import org.apache.ambari.server.api.services.serializers.JsonSerializer; +import org.apache.ambari.server.api.services.serializers.ResultSerializer; +import org.apache.ambari.server.controller.spi.Predicate; +import org.apache.ambari.server.controller.spi.Resource; +import org.apache.ambari.server.controller.spi.TemporalInfo; +import org.apache.ambari.server.events.publishers.AmbariEventPublisher; +import org.apache.ambari.server.orm.InMemoryDefaultTestModule; +import org.apache.ambari.server.orm.dao.HostInfoSummaryDAO; +import org.apache.ambari.server.orm.dao.HostInfoSummaryDTO; +import org.apache.ambari.server.view.ViewRegistry; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.security.core.context.SecurityContextHolder; + +import com.google.inject.Binder; +import com.google.inject.Guice; +import com.google.inject.Module; +import com.google.inject.util.Modules; + +public class HostInfoSummaryResourceProviderTest { + + private HostInfoSummaryDAO hostInfoSummaryDAO = null; + private AmbariEventPublisher publisher; + + @Before + public void before() { + SecurityContextHolder.getContext().setAuthentication(null); + + hostInfoSummaryDAO = createStrictMock(HostInfoSummaryDAO.class); + Guice.createInjector(Modules.override( + new InMemoryDefaultTestModule()).with(new MockModule())); + + publisher = createNiceMock(AmbariEventPublisher.class); + } + + @Test + public void testGetResourcesWithClusterName() throws Exception { + String uri = "http://xxx.com/api/v1/clusters/c1/hosts?format=summary"; + testGetResources(uri); + } + + @Test + public void testGetResourcesWithoutClusterName() throws Exception { + String uri = "http://xxx.com/api/v1/hosts?format=summary"; + testGetResources(uri); + } + + private void testGetResources(String clusterName) throws Exception { + GetRequest readRequest = createNiceMock(GetRequest.class); + HostInfoSummaryRenderer renderer = new HostInfoSummaryRenderer(); + expect(readRequest.getRenderer()).andReturn(renderer).anyTimes(); + PredicateCompiler predicateCompiler = new PredicateCompiler(); + Predicate predicate = predicateCompiler.compile("/api/v1/hosts?format=summary"); + expect(readRequest.getQueryPredicate()).andReturn(null).anyTimes(); + ResourceImpl resource = new ResourceImpl(Resource.Type.HostSummary); + Map mapIds = new HashMap<>(); + mapIds.put(Resource.Type.Host, null); + mapIds.put(Resource.Type.Cluster, "c1"); + ResourceInstanceFactory resourceFactory = new ResourceInstanceFactoryImpl(); + ResourceInstance resourceInstance = resourceFactory.createResource(Resource.Type.HostSummary, mapIds); + expect(readRequest.getResource()).andReturn(resourceInstance).anyTimes(); + + expect(readRequest.getURI()).andReturn(clusterName).anyTimes(); + Map fields = new HashMap<>(); + expect(readRequest.getFields()).andReturn(fields).anyTimes(); + List queryResults = new ArrayList<>(); + queryResults.add(new HostInfoSummaryDTO("redhat6", 10)); + queryResults.add(new HostInfoSummaryDTO("debian7", 20)); + expect(hostInfoSummaryDAO.findHostInfoSummary("c1")).andReturn(queryResults).anyTimes(); + expect(hostInfoSummaryDAO.findHostInfoSummary(null)).andReturn(queryResults).anyTimes(); + + replay(publisher, hostInfoSummaryDAO, readRequest); + + ViewRegistry.initInstance(new ViewRegistry(publisher)); + RequestHandler requestHandler = new ReadHandler(); + Result result = requestHandler.handleRequest(readRequest); + + Assert.assertEquals(ResultStatus.STATUS.OK, result.getStatus().getStatus()); + + verify(publisher, hostInfoSummaryDAO, readRequest); + } + + /** + * + */ + private class MockModule implements Module { + /** + * + */ + @Override + public void configure(Binder binder) { + binder.bind(HostInfoSummaryDAO.class).toInstance(hostInfoSummaryDAO); + } + } +} From 9ed46e303197d95f0ff2b5fbfa17922f8a9d7a5e Mon Sep 17 00:00:00 2001 From: Scott Duan Date: Tue, 6 Feb 2018 17:29:35 -0800 Subject: [PATCH 2/3] A new REST API: GET /api/v1/hosts?format=summary or /api/v1/clusters/{cluster_name}/hosts?format=summary is supported with this implementation and return the number of hosts running each possible operating system. More aggregation info of hosts can be also added in this code structure. (Rebased) --- .../server/api/handlers/ReadHandler.java | 17 +++ .../query/render/HostInfoSummaryRenderer.java | 95 ------------ .../DetachedHostResourceDefinition.java | 8 + .../HostInfoSummaryResourceDefinition.java | 65 --------- .../api/resources/HostResourceDefinition.java | 7 + .../ResourceInstanceFactoryImpl.java | 4 - .../server/api/services/HostService.java | 20 +-- .../internal/DefaultProviderModule.java | 2 - .../HostInfoSummaryResourceProvider.java | 123 ---------------- .../internal/HostResourceProvider.java | 55 ++++++- .../server/controller/spi/Resource.java | 4 +- .../server/orm/models/HostInfoSummary.java | 8 +- .../server/api/services/HostServiceTest.java | 18 +-- .../HostInfoSummaryResourceProviderTest.java | 138 ------------------ .../internal/HostResourceProviderTest.java | 62 ++++++++ 15 files changed, 162 insertions(+), 464 deletions(-) delete mode 100644 ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java delete mode 100644 ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java delete mode 100644 ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java delete mode 100644 ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java index 077c1cc0a0f..8b0316137be 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java @@ -26,6 +26,7 @@ import org.apache.ambari.server.api.services.Result; import org.apache.ambari.server.api.services.ResultImpl; import org.apache.ambari.server.api.services.ResultStatus; +import org.apache.ambari.server.controller.internal.HostResourceProvider; import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; import org.apache.ambari.server.controller.spi.NoSuchResourceException; import org.apache.ambari.server.controller.spi.Predicate; @@ -33,6 +34,7 @@ import org.apache.ambari.server.controller.spi.TemporalInfo; import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; import org.apache.ambari.server.security.authorization.AuthorizationException; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,6 +70,8 @@ public Result handleRequest(Request request) { return new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST, e.getMessage())); } + addSummaryQueryToQuery(request, query); + Result result; Predicate p = null; try { @@ -121,4 +125,17 @@ private void addFieldsToQuery(Request request, Query query) { query.addProperty(propertyId, entry.getValue()); } } + + /** + * FORMAT predicate is ignored, but if it is format=summary, it should be added to queryProperty in Read Request + * + * @param request the current request + * @param query the associated query + */ + private void addSummaryQueryToQuery(Request request, Query query) { + String uriString = request.getURI(); + if (StringUtils.isNotBlank(uriString) && uriString.indexOf("format=summary") != -1) { + query.addLocalProperty(HostResourceProvider.HOST_SUMMARY_PROPERTY_ID); + } + } } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java deleted file mode 100644 index aae1f801f22..00000000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.api.query.render; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.ambari.server.api.query.QueryInfo; -import org.apache.ambari.server.api.services.Request; -import org.apache.ambari.server.api.services.Result; -import org.apache.ambari.server.api.services.ResultImpl; -import org.apache.ambari.server.api.services.ResultPostProcessor; -import org.apache.ambari.server.api.services.ResultPostProcessorImpl; -import org.apache.ambari.server.api.util.TreeNode; -import org.apache.ambari.server.api.util.TreeNodeImpl; -import org.apache.ambari.server.controller.internal.HostInfoSummaryResourceProvider; -import org.apache.ambari.server.controller.internal.ResourceImpl; -import org.apache.ambari.server.controller.spi.Resource; -import org.apache.commons.lang.StringUtils; - -public class HostInfoSummaryRenderer extends BaseRenderer implements Renderer { - - /** - * {@inheritDoc} - */ - @Override - public TreeNode> finalizeProperties( - TreeNode queryTree, boolean isCollection) { - - QueryInfo queryInfo = queryTree.getObject(); - TreeNode> resultTree = new TreeNodeImpl<>( - null, queryInfo.getProperties(), queryTree.getName()); - - copyPropertiesToResult(queryTree, resultTree); - - return resultTree; - } - - @Override - public boolean requiresPropertyProviderInput() { - return false; - } - - @Override - public Result finalizeResult(Result queryResult) { - // Convert fully qualified properties into short property names and flat the queryResult datastructure - Map summaryMap = new HashMap<>(); - TreeNode resultTree = queryResult.getResultTree(); - Collection> nodes = resultTree.getChildren(); - if (nodes != null && !nodes.isEmpty()) { - Resource resource = (Resource)((TreeNode)nodes.iterator().next()).getObject(); - Object o = resource.getPropertyValue(HostInfoSummaryResourceProvider.CLUSTER_NAME); - if (o != null && o instanceof String && StringUtils.isNotBlank((String)o)) { - summaryMap.put("cluster_name", (String)o); - } - o = resource.getPropertiesMap().get(HostInfoSummaryResourceProvider.HOSTS_SUMMARY); - if (o != null && o instanceof Map && ((Map)o).size() > 0) { - summaryMap.putAll((Map)o); - } - } - - Resource resultResource = new ResourceImpl(Resource.Type.HostSummary); - resultResource.setProperty("hosts_summary", summaryMap); - Result summaryResult = new ResultImpl(true); - TreeNode summaryTree = summaryResult.getResultTree(); - summaryTree.addChild(resultResource, "hosts_summary"); - - return summaryResult; - } - - @Override - public ResultPostProcessor getResultPostProcessor(Request request) { - // simply return the native rendering - return new ResultPostProcessorImpl(request); - } -} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/DetachedHostResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/DetachedHostResourceDefinition.java index 1cc42118c6d..5daa3eb1bb6 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/DetachedHostResourceDefinition.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/DetachedHostResourceDefinition.java @@ -19,6 +19,8 @@ package org.apache.ambari.server.api.resources; +import org.apache.ambari.server.api.query.render.DefaultRenderer; +import org.apache.ambari.server.api.query.render.Renderer; import org.apache.ambari.server.controller.spi.Resource; @@ -40,4 +42,10 @@ public String getPluralName() { public String getSingularName() { return "host"; } + + @Override + public Renderer getRenderer(String name) { + return new DefaultRenderer(); + } + } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java deleted file mode 100644 index 74b69bc8c94..00000000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.api.resources; - -import java.util.Collection; -import java.util.Collections; - -import org.apache.ambari.server.api.query.render.HostInfoSummaryRenderer; -import org.apache.ambari.server.api.query.render.Renderer; -import org.apache.ambari.server.controller.spi.Request; -import org.apache.ambari.server.controller.spi.Resource; - -public class HostInfoSummaryResourceDefinition extends BaseResourceDefinition { - /** - * Constructor. - */ - public HostInfoSummaryResourceDefinition() { - super(Resource.Type.HostSummary); - } - - /** - * {@inheritDoc} - */ - @Override - public String getPluralName() { - return "hostInfoSummary"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getSingularName() { - return "hostInfoSummary"; - } - - @Override - public Collection getDeleteDirectives() { - return Collections.singleton(Request.DIRECTIVE_DRY_RUN); - } - - /** - * {@inheritDoc} - */ - @Override - public Renderer getRenderer(String name) { - return new HostInfoSummaryRenderer(); - } -} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java index d22b5f67479..d4018dd9216 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java @@ -24,6 +24,8 @@ import java.util.HashSet; import java.util.Set; +import org.apache.ambari.server.api.query.render.DefaultRenderer; +import org.apache.ambari.server.api.query.render.Renderer; import org.apache.ambari.server.controller.spi.Request; import org.apache.ambari.server.controller.spi.Resource; @@ -54,6 +56,11 @@ public Collection getDeleteDirectives() { return Collections.singleton(Request.DIRECTIVE_DRY_RUN); } + @Override + public Renderer getRenderer(String name) { + return new DefaultRenderer(); + } + @Override public Set getSubResourceDefinitions() { Set subs = new HashSet<>(); diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java index e4d2b094028..7d16105256b 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java @@ -535,10 +535,6 @@ public static ResourceDefinition getResourceDefinition(Resource.Type type, Map mapIds = new HashMap<>(); mapIds.put(Resource.Type.Host, hostName); if (clusterName != null) { mapIds.put(Resource.Type.Cluster, clusterName); } - return createResource(type, mapIds); + return createResource(Resource.Type.Host, mapIds); } } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java index 52803bc62e4..b7f25013980 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java @@ -134,8 +134,6 @@ protected ResourceProvider createResourceProvider(Resource.Type type) { return new RemoteClusterResourceProvider(); case Host: return new HostResourceProvider(managementController); - case HostSummary: - return new HostInfoSummaryResourceProvider(managementController); default: LOGGER.debug("Delegating creation of resource provider for: {} to the AbstractControllerResourceProvider", type.getInternalType()); return AbstractControllerResourceProvider.getResourceProvider(type, managementController); diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java deleted file mode 100644 index fec56a5100f..00000000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.ambari.server.controller.internal; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - - -import org.apache.ambari.server.controller.AmbariManagementController; -import org.apache.ambari.server.controller.spi.ExtendedResourceProvider; -import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; -import org.apache.ambari.server.controller.spi.NoSuchResourceException; -import org.apache.ambari.server.controller.spi.Predicate; -import org.apache.ambari.server.controller.spi.QueryResponse; -import org.apache.ambari.server.controller.spi.Request; -import org.apache.ambari.server.controller.spi.Resource; -import org.apache.ambari.server.controller.spi.SystemException; -import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; -import org.apache.ambari.server.orm.models.HostInfoSummary; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - - -public class HostInfoSummaryResourceProvider extends ReadOnlyResourceProvider implements ExtendedResourceProvider { - - private static final Logger LOG = LoggerFactory.getLogger(HostInfoSummaryResourceProvider.class); - - public static final String HOSTS_SUMMARY = "HostsSummary/hosts_summary"; - public static final String CLUSTER_NAME = "HostsSummary/cluster_name"; - - private static final Set PROPERTY_IDS = new HashSet<>(); - - private static final Map KEY_PROPERTY_IDS = new HashMap<>(); - - static { - PROPERTY_IDS.add(CLUSTER_NAME); - PROPERTY_IDS.add(HOSTS_SUMMARY); - KEY_PROPERTY_IDS.put(Resource.Type.Cluster, CLUSTER_NAME); - KEY_PROPERTY_IDS.put(Resource.Type.HostSummary, HOSTS_SUMMARY); - } - - /** - * Constructor. - * - * @param controller - */ - HostInfoSummaryResourceProvider(AmbariManagementController controller) { - super(Resource.Type.HostSummary, PROPERTY_IDS, KEY_PROPERTY_IDS, controller); - } - - /** - * {@inheritDoc} - */ - @Override - public QueryResponse queryForResources(Request request, Predicate predicate) - throws SystemException, UnsupportedPropertyException, NoSuchResourceException, - NoSuchParentResourceException { - - return new QueryResponseImpl(getResources(request, predicate)); - } - - /** - * {@inheritDoc} - */ - @Override - public Set getResources(Request request, Predicate predicate) - throws SystemException, UnsupportedPropertyException, - NoSuchResourceException, NoSuchParentResourceException { - - Set requestPropertyIds = getRequestPropertyIds(request, predicate); - - // use a collection which preserves order since JPA sorts the results - Set results = new LinkedHashSet<>(); - - HostInfoSummary hostInfoSummary = new HostInfoSummary(); - Resource resource = new ResourceImpl(Resource.Type.HostSummary); - // predicate may or may not be there depending on how to query - // if it has cluster name, the host summary is cluster scope - // otherwise, the host summary covers all the hosts cross the clusters - Set> propertyMap = getPropertyMaps(predicate); - String clusterName = null; - if (propertyMap.size() != 0) { - for (Map property : propertyMap) { - clusterName = (String) property.get(CLUSTER_NAME); - if (StringUtils.isNotBlank(clusterName)) { - setResourceProperty(resource, CLUSTER_NAME, clusterName, requestPropertyIds); - break; - } - } - } - hostInfoSummary = hostInfoSummary.getHostInfoSummary(clusterName); - setResourceProperty(resource, HOSTS_SUMMARY, hostInfoSummary.getSummary(), requestPropertyIds); - results.add(resource); - - return results; - } - - - @Override - protected Set getPKPropertyIds() { - return new HashSet(); - } -} 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 7ecbfdc13bc..e2518ebd394 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 @@ -51,6 +51,7 @@ import org.apache.ambari.server.controller.spi.SystemException; import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; import org.apache.ambari.server.controller.utilities.PropertyHelper; +import org.apache.ambari.server.orm.models.HostInfoSummary; import org.apache.ambari.server.security.authorization.AuthorizationException; import org.apache.ambari.server.security.authorization.AuthorizationHelper; import org.apache.ambari.server.security.authorization.ResourceType; @@ -116,6 +117,7 @@ public class HostResourceProvider extends AbstractControllerResourceProvider { public static final String STATE_PROPERTY_ID = "host_state"; public static final String TOTAL_MEM_PROPERTY_ID = "total_mem"; public static final String ATTRIBUTES_PROPERTY_ID = "attributes"; + public static final String SUMMARY_PROPERTY_ID = "summary"; public static final String HOST_CLUSTER_NAME_PROPERTY_ID = RESPONSE_KEY + PropertyHelper.EXTERNAL_PATH_SEP + CLUSTER_NAME_PROPERTY_ID; public static final String HOST_CPU_COUNT_PROPERTY_ID = RESPONSE_KEY + PropertyHelper.EXTERNAL_PATH_SEP + CPU_COUNT_PROPERTY_ID; @@ -139,7 +141,8 @@ public class HostResourceProvider extends AbstractControllerResourceProvider { public static final String HOST_RECOVERY_SUMMARY_PROPERTY_ID = RESPONSE_KEY + PropertyHelper.EXTERNAL_PATH_SEP + RECOVERY_SUMMARY_PROPERTY_ID; public static final String HOST_STATE_PROPERTY_ID = RESPONSE_KEY + PropertyHelper.EXTERNAL_PATH_SEP + STATE_PROPERTY_ID; public static final String HOST_TOTAL_MEM_PROPERTY_ID = RESPONSE_KEY + PropertyHelper.EXTERNAL_PATH_SEP + TOTAL_MEM_PROPERTY_ID; - public static final String HOST_ATTRIBUTES_PROPERTY_ID = PropertyHelper.getPropertyId(RESPONSE_KEY,ATTRIBUTES_PROPERTY_ID); + public static final String HOST_ATTRIBUTES_PROPERTY_ID = PropertyHelper.getPropertyId(RESPONSE_KEY, ATTRIBUTES_PROPERTY_ID); + public static final String HOST_SUMMARY_PROPERTY_ID = PropertyHelper.getPropertyId(RESPONSE_KEY, SUMMARY_PROPERTY_ID); public static final String BLUEPRINT_PROPERTY_ID = "blueprint"; public static final String HOST_GROUP_PROPERTY_ID = "host_group"; @@ -182,7 +185,8 @@ public class HostResourceProvider extends AbstractControllerResourceProvider { HOST_RECOVERY_SUMMARY_PROPERTY_ID, HOST_STATE_PROPERTY_ID, HOST_TOTAL_MEM_PROPERTY_ID, - HOST_ATTRIBUTES_PROPERTY_ID); + HOST_ATTRIBUTES_PROPERTY_ID, + HOST_SUMMARY_PROPERTY_ID); @Inject private OsFamily osFamily; @@ -195,7 +199,7 @@ public class HostResourceProvider extends AbstractControllerResourceProvider { /** * Create a new resource provider for the given management controller. * - * @param managementController the management controller + * @param managementController the management controller */ @AssistedInject HostResourceProvider(@Assisted AmbariManagementController managementController) { @@ -214,9 +218,9 @@ public class HostResourceProvider extends AbstractControllerResourceProvider { @Override protected RequestStatus createResourcesAuthorized(final Request request) throws SystemException, - UnsupportedPropertyException, - ResourceAlreadyExistsException, - NoSuchParentResourceException { + UnsupportedPropertyException, + ResourceAlreadyExistsException, + NoSuchParentResourceException { RequestStatusResponse createResponse = null; if (isHostGroupRequest(request)) { @@ -238,6 +242,45 @@ public Void invoke() throws AmbariException, AuthorizationException { @Override protected Set getResourcesAuthorized(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { + if (request.getPropertyIds().contains(HOST_SUMMARY_PROPERTY_ID)) { + return getHostSummaryResource(request, predicate); + } else { + return getHostResource(request, predicate); + } + } + + private Set getHostSummaryResource(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { + Set requestPropertyIds = getRequestPropertyIds(request, predicate); + + // use a collection which preserves order since JPA sorts the results + Set resources = new HashSet<>(); + + HostInfoSummary hostInfoSummary = new HostInfoSummary(); + Resource resource = new ResourceImpl(Resource.Type.Host); + // predicate may or may not be there depending on how to query + // if it has cluster name, the host summary is cluster scope + // otherwise, the host summary covers all the hosts cross the clusters + Set> propertyMap = getPropertyMaps(predicate); + String clusterName = null; + if (propertyMap.size() != 0) { + for (Map property : propertyMap) { + clusterName = (String) property.get(HOST_CLUSTER_NAME_PROPERTY_ID); + if (StringUtils.isNotBlank(clusterName)) { + setResourceProperty(resource, HOST_CLUSTER_NAME_PROPERTY_ID, clusterName, requestPropertyIds); + break; + } + } + } + hostInfoSummary = hostInfoSummary.getHostInfoSummary(clusterName); + setResourceProperty(resource, HOST_SUMMARY_PROPERTY_ID, hostInfoSummary.getSummary(), requestPropertyIds); + resources.add(resource); + + return resources; + } + + private Set getHostResource(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { final Set requests = new HashSet<>(); diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java index c6fce600577..1524d9aff27 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java @@ -175,8 +175,7 @@ enum InternalType { VersionDefinition, ClusterKerberosDescriptor, LoggingQuery, - RemoteCluster, - HostSummary; + RemoteCluster; /** * Get the {@link Type} that corresponds to this InternalType. @@ -313,7 +312,6 @@ final class Type implements Comparable{ public static final Type ClusterKerberosDescriptor = InternalType.ClusterKerberosDescriptor.getType(); public static final Type LoggingQuery = InternalType.LoggingQuery.getType(); public static final Type RemoteCluster = InternalType.RemoteCluster.getType(); - public static final Type HostSummary = InternalType.HostSummary.getType(); /** diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java index 4ead73d990c..e7880e1df3c 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java @@ -40,7 +40,7 @@ public class HostInfoSummary { @Inject private static HostInfoSummaryDAO hostInfoSummaryDAO; - private Map summary = new HashMap(); + private List summary = new ArrayList<>(); public HostInfoSummary getHostInfoSummary(String cluster_name) { @@ -49,11 +49,13 @@ public HostInfoSummary getHostInfoSummary(String cluster_name) { for (HostInfoSummaryDTO summaryDTO : summaryDTOS) { osSummaryList.add(Stream.of(new AbstractMap.SimpleImmutableEntry<>(summaryDTO.getOsType(), summaryDTO.getOsTypeCount())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); } - summary.put(HOST_INFO_SUMMARY_OS, osSummaryList); + Map os = new HashMap<>(); + os.put(HOST_INFO_SUMMARY_OS, osSummaryList); + summary.add(os); return this; } - public Map getSummary() { + public List getSummary() { return summary; } diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java index 042fad4a0d0..dcc20e9145d 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java @@ -49,20 +49,8 @@ public List getTestInvocations() throws Exception { //getHosts service = new TestHostService("clusterName", null); - m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class, String.class); - args = new Object[] {null, getHttpHeaders(), getUriInfo(), null}; - listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null)); - - //getHostsSummary - service = new TestHostService("clusterName", null); - m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class, String.class); - args = new Object[] {null, getHttpHeaders(), getUriInfo(), "summary"}; - listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null)); - - //getHostsSummary - service = new TestHostService(null, null); - m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class, String.class); - args = new Object[] {null, getHttpHeaders(), getUriInfo(), "summary"}; + m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class); + args = new Object[] {null, getHttpHeaders(), getUriInfo()}; listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null)); //createHost @@ -109,7 +97,7 @@ private TestHostService(String clusterId, String hostId) { } @Override - protected ResourceInstance createHostResource(String clusterName, String hostName, Resource.Type type) { + protected ResourceInstance createHostResource(String clusterName, String hostName) { assertEquals(m_clusterId, clusterName); assertEquals(m_hostId, hostName); return getTestResource(); diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java deleted file mode 100644 index d8b26c703a9..00000000000 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.ambari.server.controller.internal; - -import static org.easymock.EasyMock.createNiceMock; -import static org.easymock.EasyMock.createStrictMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.ws.rs.core.Response; - -import org.apache.ambari.server.api.handlers.ReadHandler; -import org.apache.ambari.server.api.handlers.RequestHandler; -import org.apache.ambari.server.api.predicate.PredicateCompiler; -import org.apache.ambari.server.api.query.render.HostInfoSummaryRenderer; -import org.apache.ambari.server.api.resources.ResourceInstance; -import org.apache.ambari.server.api.resources.ResourceInstanceFactory; -import org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl; -import org.apache.ambari.server.api.services.GetRequest; -import org.apache.ambari.server.api.services.Result; -import org.apache.ambari.server.api.services.ResultStatus; -import org.apache.ambari.server.api.services.serializers.JsonSerializer; -import org.apache.ambari.server.api.services.serializers.ResultSerializer; -import org.apache.ambari.server.controller.spi.Predicate; -import org.apache.ambari.server.controller.spi.Resource; -import org.apache.ambari.server.controller.spi.TemporalInfo; -import org.apache.ambari.server.events.publishers.AmbariEventPublisher; -import org.apache.ambari.server.orm.InMemoryDefaultTestModule; -import org.apache.ambari.server.orm.dao.HostInfoSummaryDAO; -import org.apache.ambari.server.orm.dao.HostInfoSummaryDTO; -import org.apache.ambari.server.view.ViewRegistry; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.springframework.security.core.context.SecurityContextHolder; - -import com.google.inject.Binder; -import com.google.inject.Guice; -import com.google.inject.Module; -import com.google.inject.util.Modules; - -public class HostInfoSummaryResourceProviderTest { - - private HostInfoSummaryDAO hostInfoSummaryDAO = null; - private AmbariEventPublisher publisher; - - @Before - public void before() { - SecurityContextHolder.getContext().setAuthentication(null); - - hostInfoSummaryDAO = createStrictMock(HostInfoSummaryDAO.class); - Guice.createInjector(Modules.override( - new InMemoryDefaultTestModule()).with(new MockModule())); - - publisher = createNiceMock(AmbariEventPublisher.class); - } - - @Test - public void testGetResourcesWithClusterName() throws Exception { - String uri = "http://xxx.com/api/v1/clusters/c1/hosts?format=summary"; - testGetResources(uri); - } - - @Test - public void testGetResourcesWithoutClusterName() throws Exception { - String uri = "http://xxx.com/api/v1/hosts?format=summary"; - testGetResources(uri); - } - - private void testGetResources(String clusterName) throws Exception { - GetRequest readRequest = createNiceMock(GetRequest.class); - HostInfoSummaryRenderer renderer = new HostInfoSummaryRenderer(); - expect(readRequest.getRenderer()).andReturn(renderer).anyTimes(); - PredicateCompiler predicateCompiler = new PredicateCompiler(); - Predicate predicate = predicateCompiler.compile("/api/v1/hosts?format=summary"); - expect(readRequest.getQueryPredicate()).andReturn(null).anyTimes(); - ResourceImpl resource = new ResourceImpl(Resource.Type.HostSummary); - Map mapIds = new HashMap<>(); - mapIds.put(Resource.Type.Host, null); - mapIds.put(Resource.Type.Cluster, "c1"); - ResourceInstanceFactory resourceFactory = new ResourceInstanceFactoryImpl(); - ResourceInstance resourceInstance = resourceFactory.createResource(Resource.Type.HostSummary, mapIds); - expect(readRequest.getResource()).andReturn(resourceInstance).anyTimes(); - - expect(readRequest.getURI()).andReturn(clusterName).anyTimes(); - Map fields = new HashMap<>(); - expect(readRequest.getFields()).andReturn(fields).anyTimes(); - List queryResults = new ArrayList<>(); - queryResults.add(new HostInfoSummaryDTO("redhat6", 10)); - queryResults.add(new HostInfoSummaryDTO("debian7", 20)); - expect(hostInfoSummaryDAO.findHostInfoSummary("c1")).andReturn(queryResults).anyTimes(); - expect(hostInfoSummaryDAO.findHostInfoSummary(null)).andReturn(queryResults).anyTimes(); - - replay(publisher, hostInfoSummaryDAO, readRequest); - - ViewRegistry.initInstance(new ViewRegistry(publisher)); - RequestHandler requestHandler = new ReadHandler(); - Result result = requestHandler.handleRequest(readRequest); - - Assert.assertEquals(ResultStatus.STATUS.OK, result.getStatus().getStatus()); - - verify(publisher, hostInfoSummaryDAO, readRequest); - } - - /** - * - */ - private class MockModule implements Module { - /** - * - */ - @Override - public void configure(Binder binder) { - binder.bind(HostInfoSummaryDAO.class).toInstance(hostInfoSummaryDAO); - } - } -} diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java index e68ff2eb03d..69721cd4ba6 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java @@ -42,6 +42,7 @@ import javax.persistence.EntityManager; + import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.HostNotFoundException; import org.apache.ambari.server.actionmanager.ActionDBAccessor; @@ -62,6 +63,9 @@ import org.apache.ambari.server.controller.utilities.PredicateBuilder; import org.apache.ambari.server.controller.utilities.PropertyHelper; import org.apache.ambari.server.orm.DBAccessor; +import org.apache.ambari.server.orm.InMemoryDefaultTestModule; +import org.apache.ambari.server.orm.dao.HostInfoSummaryDAO; +import org.apache.ambari.server.orm.dao.HostInfoSummaryDTO; import org.apache.ambari.server.scheduler.ExecutionScheduler; import org.apache.ambari.server.security.TestAuthenticationFactory; import org.apache.ambari.server.security.authorization.AuthorizationException; @@ -89,8 +93,11 @@ import com.google.gson.Gson; import com.google.inject.AbstractModule; +import com.google.inject.Binder; import com.google.inject.Guice; import com.google.inject.Injector; +import com.google.inject.Module; +import com.google.inject.util.Modules; /** * HostResourceProvider tests. @@ -1283,6 +1290,61 @@ public void testGetHosts___OR_Predicate_HostNotFoundException() throws Exception verifyAll(); } + @Test + public void testHostsSummary() throws Exception { + Injector injector = createInjector(); + AmbariManagementController managementController = injector.getInstance(AmbariManagementController.class); + ResourceProvider hostResourceProvider = getHostProvider(injector); + + injector = Guice.createInjector(Modules.override( + new InMemoryDefaultTestModule()).with(new HostResourceProviderTest.MockModule())); + HostInfoSummaryDAO hostInfoSummaryDAO = injector.getInstance(HostInfoSummaryDAO.class); + List queryResults = new ArrayList<>(); + queryResults.add(new HostInfoSummaryDTO("redhat6", 10)); + queryResults.add(new HostInfoSummaryDTO("debian7", 20)); + expect(hostInfoSummaryDAO.findHostInfoSummary("c1")).andReturn(queryResults).anyTimes(); + + Set propertyIds = new HashSet<>(); + + propertyIds.add(HostResourceProvider.HOST_CLUSTER_NAME_PROPERTY_ID); + propertyIds.add(HostResourceProvider.HOST_SUMMARY_PROPERTY_ID); + + Predicate predicate = buildPredicate("c1", null); + Request request = PropertyHelper.getReadRequest(propertyIds); + + replayAll(); + + SecurityContextHolder.getContext().setAuthentication(TestAuthenticationFactory.createAdministrator()); + + Set resources = hostResourceProvider.getResources(request, predicate); + + Assert.assertEquals(1, resources.size()); + for (Resource resource : resources) { + Map propertyMap = (Map)resource.getPropertiesMap().values().iterator().next(); + String clusterName = (String)(propertyMap.get("cluster_name")); + Assert.assertEquals("c1", clusterName); + Map summaryMap = (Map)((List)propertyMap.get("summary")).get(0); + Assert.assertEquals(1, summaryMap.size()); + List> osSummary = (List>)summaryMap.get("operating_systems"); + Assert.assertEquals(10, (int)osSummary.get(0).get("redhat6")); + Assert.assertEquals(20, (int)osSummary.get(1).get("debian7")); + } + + verifyAll(); + } + + /** + * A class to generate in-memory singleton instance for object + */ + private class MockModule implements Module { + + @Override + public void configure(Binder binder) { + binder.bind(HostInfoSummaryDAO.class).toInstance(createStrictMock(HostInfoSummaryDAO.class)); + // We can add more + } + } + public static void createHosts(AmbariManagementController controller, Set requests) throws AmbariException, AuthorizationException { HostResourceProvider provider = getHostProvider(controller); Set> properties = new HashSet<>(); From a0554e4c96e62b5767fe7b016cead1ba3aa00812 Mon Sep 17 00:00:00 2001 From: Scott Duan Date: Tue, 6 Feb 2018 17:29:35 -0800 Subject: [PATCH 3/3] A new REST API: GET /api/v1/hosts?format=summary or /api/v1/clusters/{cluster_name}/hosts?format=summary is supported with this implementation and return the number of hosts running each possible operating system. More aggregation info of hosts can be also added in this code structure. (Rebased) --- .../server/api/handlers/ReadHandler.java | 17 +++ .../query/render/HostInfoSummaryRenderer.java | 95 ------------ .../DetachedHostResourceDefinition.java | 8 + .../HostInfoSummaryResourceDefinition.java | 65 --------- .../api/resources/HostResourceDefinition.java | 7 + .../ResourceInstanceFactoryImpl.java | 4 - .../server/api/services/HostService.java | 20 +-- .../server/controller/HostResponse.java | 38 +++++ .../internal/DefaultProviderModule.java | 2 - .../HostInfoSummaryResourceProvider.java | 123 ---------------- .../internal/HostResourceProvider.java | 54 ++++++- .../server/controller/spi/Resource.java | 4 +- .../server/orm/dao/HostInfoSummaryDAO.java | 4 - .../server/orm/dao/HostInfoSummaryDTO.java | 14 +- .../entities/ClusterHostMappingEntity.java | 9 +- .../entities/ClusterHostMappingEntityPK.java | 70 --------- .../server/orm/models/HostInfoSummary.java | 19 +-- .../server/api/services/HostServiceTest.java | 19 +-- .../HostInfoSummaryResourceProviderTest.java | 138 ------------------ .../internal/HostResourceProviderTest.java | 62 ++++++++ 20 files changed, 212 insertions(+), 560 deletions(-) delete mode 100644 ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java delete mode 100644 ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java delete mode 100644 ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java delete mode 100644 ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntityPK.java delete mode 100644 ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java index 077c1cc0a0f..8b0316137be 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/handlers/ReadHandler.java @@ -26,6 +26,7 @@ import org.apache.ambari.server.api.services.Result; import org.apache.ambari.server.api.services.ResultImpl; import org.apache.ambari.server.api.services.ResultStatus; +import org.apache.ambari.server.controller.internal.HostResourceProvider; import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; import org.apache.ambari.server.controller.spi.NoSuchResourceException; import org.apache.ambari.server.controller.spi.Predicate; @@ -33,6 +34,7 @@ import org.apache.ambari.server.controller.spi.TemporalInfo; import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; import org.apache.ambari.server.security.authorization.AuthorizationException; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,6 +70,8 @@ public Result handleRequest(Request request) { return new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST, e.getMessage())); } + addSummaryQueryToQuery(request, query); + Result result; Predicate p = null; try { @@ -121,4 +125,17 @@ private void addFieldsToQuery(Request request, Query query) { query.addProperty(propertyId, entry.getValue()); } } + + /** + * FORMAT predicate is ignored, but if it is format=summary, it should be added to queryProperty in Read Request + * + * @param request the current request + * @param query the associated query + */ + private void addSummaryQueryToQuery(Request request, Query query) { + String uriString = request.getURI(); + if (StringUtils.isNotBlank(uriString) && uriString.indexOf("format=summary") != -1) { + query.addLocalProperty(HostResourceProvider.HOST_SUMMARY_PROPERTY_ID); + } + } } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java deleted file mode 100644 index aae1f801f22..00000000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostInfoSummaryRenderer.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.api.query.render; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.ambari.server.api.query.QueryInfo; -import org.apache.ambari.server.api.services.Request; -import org.apache.ambari.server.api.services.Result; -import org.apache.ambari.server.api.services.ResultImpl; -import org.apache.ambari.server.api.services.ResultPostProcessor; -import org.apache.ambari.server.api.services.ResultPostProcessorImpl; -import org.apache.ambari.server.api.util.TreeNode; -import org.apache.ambari.server.api.util.TreeNodeImpl; -import org.apache.ambari.server.controller.internal.HostInfoSummaryResourceProvider; -import org.apache.ambari.server.controller.internal.ResourceImpl; -import org.apache.ambari.server.controller.spi.Resource; -import org.apache.commons.lang.StringUtils; - -public class HostInfoSummaryRenderer extends BaseRenderer implements Renderer { - - /** - * {@inheritDoc} - */ - @Override - public TreeNode> finalizeProperties( - TreeNode queryTree, boolean isCollection) { - - QueryInfo queryInfo = queryTree.getObject(); - TreeNode> resultTree = new TreeNodeImpl<>( - null, queryInfo.getProperties(), queryTree.getName()); - - copyPropertiesToResult(queryTree, resultTree); - - return resultTree; - } - - @Override - public boolean requiresPropertyProviderInput() { - return false; - } - - @Override - public Result finalizeResult(Result queryResult) { - // Convert fully qualified properties into short property names and flat the queryResult datastructure - Map summaryMap = new HashMap<>(); - TreeNode resultTree = queryResult.getResultTree(); - Collection> nodes = resultTree.getChildren(); - if (nodes != null && !nodes.isEmpty()) { - Resource resource = (Resource)((TreeNode)nodes.iterator().next()).getObject(); - Object o = resource.getPropertyValue(HostInfoSummaryResourceProvider.CLUSTER_NAME); - if (o != null && o instanceof String && StringUtils.isNotBlank((String)o)) { - summaryMap.put("cluster_name", (String)o); - } - o = resource.getPropertiesMap().get(HostInfoSummaryResourceProvider.HOSTS_SUMMARY); - if (o != null && o instanceof Map && ((Map)o).size() > 0) { - summaryMap.putAll((Map)o); - } - } - - Resource resultResource = new ResourceImpl(Resource.Type.HostSummary); - resultResource.setProperty("hosts_summary", summaryMap); - Result summaryResult = new ResultImpl(true); - TreeNode summaryTree = summaryResult.getResultTree(); - summaryTree.addChild(resultResource, "hosts_summary"); - - return summaryResult; - } - - @Override - public ResultPostProcessor getResultPostProcessor(Request request) { - // simply return the native rendering - return new ResultPostProcessorImpl(request); - } -} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/DetachedHostResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/DetachedHostResourceDefinition.java index 1cc42118c6d..5daa3eb1bb6 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/DetachedHostResourceDefinition.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/DetachedHostResourceDefinition.java @@ -19,6 +19,8 @@ package org.apache.ambari.server.api.resources; +import org.apache.ambari.server.api.query.render.DefaultRenderer; +import org.apache.ambari.server.api.query.render.Renderer; import org.apache.ambari.server.controller.spi.Resource; @@ -40,4 +42,10 @@ public String getPluralName() { public String getSingularName() { return "host"; } + + @Override + public Renderer getRenderer(String name) { + return new DefaultRenderer(); + } + } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java deleted file mode 100644 index 74b69bc8c94..00000000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostInfoSummaryResourceDefinition.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.api.resources; - -import java.util.Collection; -import java.util.Collections; - -import org.apache.ambari.server.api.query.render.HostInfoSummaryRenderer; -import org.apache.ambari.server.api.query.render.Renderer; -import org.apache.ambari.server.controller.spi.Request; -import org.apache.ambari.server.controller.spi.Resource; - -public class HostInfoSummaryResourceDefinition extends BaseResourceDefinition { - /** - * Constructor. - */ - public HostInfoSummaryResourceDefinition() { - super(Resource.Type.HostSummary); - } - - /** - * {@inheritDoc} - */ - @Override - public String getPluralName() { - return "hostInfoSummary"; - } - - /** - * {@inheritDoc} - */ - @Override - public String getSingularName() { - return "hostInfoSummary"; - } - - @Override - public Collection getDeleteDirectives() { - return Collections.singleton(Request.DIRECTIVE_DRY_RUN); - } - - /** - * {@inheritDoc} - */ - @Override - public Renderer getRenderer(String name) { - return new HostInfoSummaryRenderer(); - } -} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java index d22b5f67479..d4018dd9216 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java @@ -24,6 +24,8 @@ import java.util.HashSet; import java.util.Set; +import org.apache.ambari.server.api.query.render.DefaultRenderer; +import org.apache.ambari.server.api.query.render.Renderer; import org.apache.ambari.server.controller.spi.Request; import org.apache.ambari.server.controller.spi.Resource; @@ -54,6 +56,11 @@ public Collection getDeleteDirectives() { return Collections.singleton(Request.DIRECTIVE_DRY_RUN); } + @Override + public Renderer getRenderer(String name) { + return new DefaultRenderer(); + } + @Override public Set getSubResourceDefinitions() { Set subs = new HashSet<>(); diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java index e4d2b094028..7d16105256b 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java @@ -535,10 +535,6 @@ public static ResourceDefinition getResourceDefinition(Resource.Type type, Map mapIds = new HashMap<>(); mapIds.put(Resource.Type.Host, hostName); if (clusterName != null) { mapIds.put(Resource.Type.Cluster, clusterName); } - return createResource(type, mapIds); + return createResource(Resource.Type.Host, mapIds); } } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/HostResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/HostResponse.java index 9d02a0bbcc5..2c40b059954 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/HostResponse.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/HostResponse.java @@ -146,6 +146,29 @@ public class HostResponse { private MaintenanceState maintenanceState; + /** + * Summary information of all hosts in a cluster or multiple clusters + * + * The response json schema is: + * { + * "Hosts" : { + * "cluster_name" : "c1", + * "summary" : [ + * { + * "operating_systems" : [ + * { + * "centos6" : 2 + * }, + * { + * "centos7" : 5 + * } + * ] + * } + * ] + * } + */ + private List hostsSummary; + public HostResponse(String hostname, String clusterName, String ipv4, int cpuCount, int phCpuCount, String osArch, String osType, long totalMemBytes, @@ -421,6 +444,21 @@ public RecoveryReport getRecoveryReport() { return recoveryReport; } + /** + * Set the hostsSummary + */ + public void setHostsSummary(List hostsSummary) { + this.hostsSummary = hostsSummary; + } + + /** + * Get the aggregation info of hosts in a cluster or in multiple clusters + */ + @ApiModelProperty(name = HostResourceProvider.SUMMARY_PROPERTY_ID) + public List getHostsSummary() { + return hostsSummary; + } + /** * Set the detailed recovery report */ diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java index 52803bc62e4..b7f25013980 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java @@ -134,8 +134,6 @@ protected ResourceProvider createResourceProvider(Resource.Type type) { return new RemoteClusterResourceProvider(); case Host: return new HostResourceProvider(managementController); - case HostSummary: - return new HostInfoSummaryResourceProvider(managementController); default: LOGGER.debug("Delegating creation of resource provider for: {} to the AbstractControllerResourceProvider", type.getInternalType()); return AbstractControllerResourceProvider.getResourceProvider(type, managementController); diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java deleted file mode 100644 index fec56a5100f..00000000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProvider.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.ambari.server.controller.internal; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - - -import org.apache.ambari.server.controller.AmbariManagementController; -import org.apache.ambari.server.controller.spi.ExtendedResourceProvider; -import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; -import org.apache.ambari.server.controller.spi.NoSuchResourceException; -import org.apache.ambari.server.controller.spi.Predicate; -import org.apache.ambari.server.controller.spi.QueryResponse; -import org.apache.ambari.server.controller.spi.Request; -import org.apache.ambari.server.controller.spi.Resource; -import org.apache.ambari.server.controller.spi.SystemException; -import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; -import org.apache.ambari.server.orm.models.HostInfoSummary; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - - -public class HostInfoSummaryResourceProvider extends ReadOnlyResourceProvider implements ExtendedResourceProvider { - - private static final Logger LOG = LoggerFactory.getLogger(HostInfoSummaryResourceProvider.class); - - public static final String HOSTS_SUMMARY = "HostsSummary/hosts_summary"; - public static final String CLUSTER_NAME = "HostsSummary/cluster_name"; - - private static final Set PROPERTY_IDS = new HashSet<>(); - - private static final Map KEY_PROPERTY_IDS = new HashMap<>(); - - static { - PROPERTY_IDS.add(CLUSTER_NAME); - PROPERTY_IDS.add(HOSTS_SUMMARY); - KEY_PROPERTY_IDS.put(Resource.Type.Cluster, CLUSTER_NAME); - KEY_PROPERTY_IDS.put(Resource.Type.HostSummary, HOSTS_SUMMARY); - } - - /** - * Constructor. - * - * @param controller - */ - HostInfoSummaryResourceProvider(AmbariManagementController controller) { - super(Resource.Type.HostSummary, PROPERTY_IDS, KEY_PROPERTY_IDS, controller); - } - - /** - * {@inheritDoc} - */ - @Override - public QueryResponse queryForResources(Request request, Predicate predicate) - throws SystemException, UnsupportedPropertyException, NoSuchResourceException, - NoSuchParentResourceException { - - return new QueryResponseImpl(getResources(request, predicate)); - } - - /** - * {@inheritDoc} - */ - @Override - public Set getResources(Request request, Predicate predicate) - throws SystemException, UnsupportedPropertyException, - NoSuchResourceException, NoSuchParentResourceException { - - Set requestPropertyIds = getRequestPropertyIds(request, predicate); - - // use a collection which preserves order since JPA sorts the results - Set results = new LinkedHashSet<>(); - - HostInfoSummary hostInfoSummary = new HostInfoSummary(); - Resource resource = new ResourceImpl(Resource.Type.HostSummary); - // predicate may or may not be there depending on how to query - // if it has cluster name, the host summary is cluster scope - // otherwise, the host summary covers all the hosts cross the clusters - Set> propertyMap = getPropertyMaps(predicate); - String clusterName = null; - if (propertyMap.size() != 0) { - for (Map property : propertyMap) { - clusterName = (String) property.get(CLUSTER_NAME); - if (StringUtils.isNotBlank(clusterName)) { - setResourceProperty(resource, CLUSTER_NAME, clusterName, requestPropertyIds); - break; - } - } - } - hostInfoSummary = hostInfoSummary.getHostInfoSummary(clusterName); - setResourceProperty(resource, HOSTS_SUMMARY, hostInfoSummary.getSummary(), requestPropertyIds); - results.add(resource); - - return results; - } - - - @Override - protected Set getPKPropertyIds() { - return new HashSet(); - } -} 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 7ecbfdc13bc..a5a4fe4a270 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 @@ -51,6 +51,7 @@ import org.apache.ambari.server.controller.spi.SystemException; import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; import org.apache.ambari.server.controller.utilities.PropertyHelper; +import org.apache.ambari.server.orm.models.HostInfoSummary; import org.apache.ambari.server.security.authorization.AuthorizationException; import org.apache.ambari.server.security.authorization.AuthorizationHelper; import org.apache.ambari.server.security.authorization.ResourceType; @@ -116,6 +117,7 @@ public class HostResourceProvider extends AbstractControllerResourceProvider { public static final String STATE_PROPERTY_ID = "host_state"; public static final String TOTAL_MEM_PROPERTY_ID = "total_mem"; public static final String ATTRIBUTES_PROPERTY_ID = "attributes"; + public static final String SUMMARY_PROPERTY_ID = "summary"; public static final String HOST_CLUSTER_NAME_PROPERTY_ID = RESPONSE_KEY + PropertyHelper.EXTERNAL_PATH_SEP + CLUSTER_NAME_PROPERTY_ID; public static final String HOST_CPU_COUNT_PROPERTY_ID = RESPONSE_KEY + PropertyHelper.EXTERNAL_PATH_SEP + CPU_COUNT_PROPERTY_ID; @@ -139,7 +141,8 @@ public class HostResourceProvider extends AbstractControllerResourceProvider { public static final String HOST_RECOVERY_SUMMARY_PROPERTY_ID = RESPONSE_KEY + PropertyHelper.EXTERNAL_PATH_SEP + RECOVERY_SUMMARY_PROPERTY_ID; public static final String HOST_STATE_PROPERTY_ID = RESPONSE_KEY + PropertyHelper.EXTERNAL_PATH_SEP + STATE_PROPERTY_ID; public static final String HOST_TOTAL_MEM_PROPERTY_ID = RESPONSE_KEY + PropertyHelper.EXTERNAL_PATH_SEP + TOTAL_MEM_PROPERTY_ID; - public static final String HOST_ATTRIBUTES_PROPERTY_ID = PropertyHelper.getPropertyId(RESPONSE_KEY,ATTRIBUTES_PROPERTY_ID); + public static final String HOST_ATTRIBUTES_PROPERTY_ID = PropertyHelper.getPropertyId(RESPONSE_KEY, ATTRIBUTES_PROPERTY_ID); + public static final String HOST_SUMMARY_PROPERTY_ID = PropertyHelper.getPropertyId(RESPONSE_KEY, SUMMARY_PROPERTY_ID); public static final String BLUEPRINT_PROPERTY_ID = "blueprint"; public static final String HOST_GROUP_PROPERTY_ID = "host_group"; @@ -182,7 +185,8 @@ public class HostResourceProvider extends AbstractControllerResourceProvider { HOST_RECOVERY_SUMMARY_PROPERTY_ID, HOST_STATE_PROPERTY_ID, HOST_TOTAL_MEM_PROPERTY_ID, - HOST_ATTRIBUTES_PROPERTY_ID); + HOST_ATTRIBUTES_PROPERTY_ID, + HOST_SUMMARY_PROPERTY_ID); @Inject private OsFamily osFamily; @@ -195,7 +199,7 @@ public class HostResourceProvider extends AbstractControllerResourceProvider { /** * Create a new resource provider for the given management controller. * - * @param managementController the management controller + * @param managementController the management controller */ @AssistedInject HostResourceProvider(@Assisted AmbariManagementController managementController) { @@ -214,9 +218,9 @@ public class HostResourceProvider extends AbstractControllerResourceProvider { @Override protected RequestStatus createResourcesAuthorized(final Request request) throws SystemException, - UnsupportedPropertyException, - ResourceAlreadyExistsException, - NoSuchParentResourceException { + UnsupportedPropertyException, + ResourceAlreadyExistsException, + NoSuchParentResourceException { RequestStatusResponse createResponse = null; if (isHostGroupRequest(request)) { @@ -238,6 +242,44 @@ public Void invoke() throws AmbariException, AuthorizationException { @Override protected Set getResourcesAuthorized(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { + if (request.getPropertyIds().contains(HOST_SUMMARY_PROPERTY_ID)) { + return getHostSummaryResource(request, predicate); + } else { + return getHostResource(request, predicate); + } + } + + private Set getHostSummaryResource(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { + Set requestPropertyIds = getRequestPropertyIds(request, predicate); + + Set resources = new HashSet<>(); + + HostInfoSummary hostInfoSummary = new HostInfoSummary(); + Resource resource = new ResourceImpl(Resource.Type.Host); + // predicate may or may not be there depending on how to query + // if it has cluster name, the host summary is cluster scope + // otherwise, the host summary covers all the hosts cross the clusters + Set> propertyMap = getPropertyMaps(predicate); + String clusterName = null; + if (propertyMap.size() != 0) { + for (Map property : propertyMap) { + clusterName = (String) property.get(HOST_CLUSTER_NAME_PROPERTY_ID); + if (StringUtils.isNotBlank(clusterName)) { + setResourceProperty(resource, HOST_CLUSTER_NAME_PROPERTY_ID, clusterName, requestPropertyIds); + break; + } + } + } + List summary = hostInfoSummary.getHostInfoSummary(clusterName); + setResourceProperty(resource, HOST_SUMMARY_PROPERTY_ID, summary, requestPropertyIds); + resources.add(resource); + + return resources; + } + + private Set getHostResource(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { final Set requests = new HashSet<>(); diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java index c6fce600577..1524d9aff27 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java @@ -175,8 +175,7 @@ enum InternalType { VersionDefinition, ClusterKerberosDescriptor, LoggingQuery, - RemoteCluster, - HostSummary; + RemoteCluster; /** * Get the {@link Type} that corresponds to this InternalType. @@ -313,7 +312,6 @@ final class Type implements Comparable{ public static final Type ClusterKerberosDescriptor = InternalType.ClusterKerberosDescriptor.getType(); public static final Type LoggingQuery = InternalType.LoggingQuery.getType(); public static final Type RemoteCluster = InternalType.RemoteCluster.getType(); - public static final Type HostSummary = InternalType.HostSummary.getType(); /** diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDAO.java index 22bd9423947..bfb4b13401a 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDAO.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDAO.java @@ -25,8 +25,6 @@ import org.apache.ambari.server.orm.RequiresSession; import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.google.inject.Inject; import com.google.inject.Provider; @@ -35,8 +33,6 @@ @Singleton public class HostInfoSummaryDAO { - private static final Logger LOG = LoggerFactory.getLogger(HostInfoSummaryDAO.class); - // Whenever we want to aggregate more host info in summary, we need to update these two statements // also we must update HostInfoSummary.java private static final String SUMMARY_WITH_CLUSTER_NAME_QUERY_STATEMENT = diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDTO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDTO.java index 19b60e1746b..ea39863e2af 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDTO.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/HostInfoSummaryDTO.java @@ -19,19 +19,19 @@ public class HostInfoSummaryDTO { - private String os_type; - private int os_type_count = 0; + private String osType; + private int osTypeCount = 0; - public HostInfoSummaryDTO(String os_type, Number os_type_count) { - this.os_type = os_type; - this.os_type_count = null == os_type_count ? 0 : os_type_count.intValue(); + public HostInfoSummaryDTO(String osType, Number osTypeCount) { + this.osType = osType; + this.osTypeCount = null == osTypeCount ? 0 : osTypeCount.intValue(); } public String getOsType() { - return os_type; + return osType; } public int getOsTypeCount() { - return os_type_count; + return osTypeCount; } } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntity.java index a6d5deb22ce..5d499cc5e99 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntity.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntity.java @@ -18,15 +18,16 @@ package org.apache.ambari.server.orm.entities; +import java.util.Objects; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; -import javax.persistence.IdClass; import javax.persistence.Table; + @Entity @Table(name = "ClusterHostMapping") -@IdClass(ClusterHostMappingEntityPK.class) public class ClusterHostMappingEntity { @Id @@ -68,8 +69,6 @@ public boolean equals(Object o) { @Override public int hashCode() { - int result = clusterId.hashCode(); - result = 31 * result + hostId.hashCode(); - return result; + return Objects.hash(clusterId, hostId); } } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntityPK.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntityPK.java deleted file mode 100644 index 9729b76b94a..00000000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterHostMappingEntityPK.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.orm.entities; - -import java.io.Serializable; - -import javax.persistence.Column; -import javax.persistence.Id; - -public class ClusterHostMappingEntityPK implements Serializable { - - private Long clusterId; - private Long hostId; - - @Id - @Column(name = "cluster_id", nullable = false, insertable = true, updatable = true) - public Long getClusterId() { - return clusterId; - } - - public void setClusterId(Long clusterId) { - this.clusterId = clusterId; - } - - @Id - @Column(name = "host_id", nullable = false, insertable = true, updatable = true) - public Long getHostId() { - return hostId; - } - - public void setHostId(Long hostId) { - this.hostId = hostId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ClusterHostMappingEntityPK that = (ClusterHostMappingEntityPK) o; - - if (!clusterId.equals(that.clusterId)) return false; - if (!hostId.equals(that.hostId)) return false; - - return true; - } - - @Override - public int hashCode() { - int result = clusterId.hashCode(); - result = 31 * result + hostId.hashCode(); - return result; - } -} diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java index 4ead73d990c..8d3ba21232a 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostInfoSummary.java @@ -18,18 +18,16 @@ package org.apache.ambari.server.orm.models; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.ambari.server.StaticallyInject; import org.apache.ambari.server.orm.dao.HostInfoSummaryDAO; import org.apache.ambari.server.orm.dao.HostInfoSummaryDTO; +import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; @StaticallyInject @@ -40,20 +38,17 @@ public class HostInfoSummary { @Inject private static HostInfoSummaryDAO hostInfoSummaryDAO; - private Map summary = new HashMap(); - - public HostInfoSummary getHostInfoSummary(String cluster_name) { + public List getHostInfoSummary(String cluster_name) { List summaryDTOS = hostInfoSummaryDAO.findHostInfoSummary(cluster_name); List> osSummaryList = new ArrayList<>(); for (HostInfoSummaryDTO summaryDTO : summaryDTOS) { - osSummaryList.add(Stream.of(new AbstractMap.SimpleImmutableEntry<>(summaryDTO.getOsType(), summaryDTO.getOsTypeCount())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); + osSummaryList.add(ImmutableMap.of(summaryDTO.getOsType(), summaryDTO.getOsTypeCount())); } - summary.put(HOST_INFO_SUMMARY_OS, osSummaryList); - return this; - } - - public Map getSummary() { + Map os = new HashMap<>(); + os.put(HOST_INFO_SUMMARY_OS, osSummaryList); + List summary = new ArrayList<>(); + summary.add(os); return summary; } diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java index 042fad4a0d0..33eb12ceef8 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java @@ -31,7 +31,6 @@ import org.apache.ambari.server.api.resources.ResourceInstance; import org.apache.ambari.server.api.services.parsers.RequestBodyParser; import org.apache.ambari.server.api.services.serializers.ResultSerializer; -import org.apache.ambari.server.controller.spi.Resource; /** * Unit tests for HostService. @@ -49,20 +48,8 @@ public List getTestInvocations() throws Exception { //getHosts service = new TestHostService("clusterName", null); - m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class, String.class); - args = new Object[] {null, getHttpHeaders(), getUriInfo(), null}; - listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null)); - - //getHostsSummary - service = new TestHostService("clusterName", null); - m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class, String.class); - args = new Object[] {null, getHttpHeaders(), getUriInfo(), "summary"}; - listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null)); - - //getHostsSummary - service = new TestHostService(null, null); - m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class, String.class); - args = new Object[] {null, getHttpHeaders(), getUriInfo(), "summary"}; + m = service.getClass().getMethod("getHosts", String.class, HttpHeaders.class, UriInfo.class); + args = new Object[] {null, getHttpHeaders(), getUriInfo()}; listInvocations.add(new ServiceTestInvocation(Request.Type.GET, service, m, args, null)); //createHost @@ -109,7 +96,7 @@ private TestHostService(String clusterId, String hostId) { } @Override - protected ResourceInstance createHostResource(String clusterName, String hostName, Resource.Type type) { + protected ResourceInstance createHostResource(String clusterName, String hostName) { assertEquals(m_clusterId, clusterName); assertEquals(m_hostId, hostName); return getTestResource(); diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java deleted file mode 100644 index d8b26c703a9..00000000000 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostInfoSummaryResourceProviderTest.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.ambari.server.controller.internal; - -import static org.easymock.EasyMock.createNiceMock; -import static org.easymock.EasyMock.createStrictMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.ws.rs.core.Response; - -import org.apache.ambari.server.api.handlers.ReadHandler; -import org.apache.ambari.server.api.handlers.RequestHandler; -import org.apache.ambari.server.api.predicate.PredicateCompiler; -import org.apache.ambari.server.api.query.render.HostInfoSummaryRenderer; -import org.apache.ambari.server.api.resources.ResourceInstance; -import org.apache.ambari.server.api.resources.ResourceInstanceFactory; -import org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl; -import org.apache.ambari.server.api.services.GetRequest; -import org.apache.ambari.server.api.services.Result; -import org.apache.ambari.server.api.services.ResultStatus; -import org.apache.ambari.server.api.services.serializers.JsonSerializer; -import org.apache.ambari.server.api.services.serializers.ResultSerializer; -import org.apache.ambari.server.controller.spi.Predicate; -import org.apache.ambari.server.controller.spi.Resource; -import org.apache.ambari.server.controller.spi.TemporalInfo; -import org.apache.ambari.server.events.publishers.AmbariEventPublisher; -import org.apache.ambari.server.orm.InMemoryDefaultTestModule; -import org.apache.ambari.server.orm.dao.HostInfoSummaryDAO; -import org.apache.ambari.server.orm.dao.HostInfoSummaryDTO; -import org.apache.ambari.server.view.ViewRegistry; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.springframework.security.core.context.SecurityContextHolder; - -import com.google.inject.Binder; -import com.google.inject.Guice; -import com.google.inject.Module; -import com.google.inject.util.Modules; - -public class HostInfoSummaryResourceProviderTest { - - private HostInfoSummaryDAO hostInfoSummaryDAO = null; - private AmbariEventPublisher publisher; - - @Before - public void before() { - SecurityContextHolder.getContext().setAuthentication(null); - - hostInfoSummaryDAO = createStrictMock(HostInfoSummaryDAO.class); - Guice.createInjector(Modules.override( - new InMemoryDefaultTestModule()).with(new MockModule())); - - publisher = createNiceMock(AmbariEventPublisher.class); - } - - @Test - public void testGetResourcesWithClusterName() throws Exception { - String uri = "http://xxx.com/api/v1/clusters/c1/hosts?format=summary"; - testGetResources(uri); - } - - @Test - public void testGetResourcesWithoutClusterName() throws Exception { - String uri = "http://xxx.com/api/v1/hosts?format=summary"; - testGetResources(uri); - } - - private void testGetResources(String clusterName) throws Exception { - GetRequest readRequest = createNiceMock(GetRequest.class); - HostInfoSummaryRenderer renderer = new HostInfoSummaryRenderer(); - expect(readRequest.getRenderer()).andReturn(renderer).anyTimes(); - PredicateCompiler predicateCompiler = new PredicateCompiler(); - Predicate predicate = predicateCompiler.compile("/api/v1/hosts?format=summary"); - expect(readRequest.getQueryPredicate()).andReturn(null).anyTimes(); - ResourceImpl resource = new ResourceImpl(Resource.Type.HostSummary); - Map mapIds = new HashMap<>(); - mapIds.put(Resource.Type.Host, null); - mapIds.put(Resource.Type.Cluster, "c1"); - ResourceInstanceFactory resourceFactory = new ResourceInstanceFactoryImpl(); - ResourceInstance resourceInstance = resourceFactory.createResource(Resource.Type.HostSummary, mapIds); - expect(readRequest.getResource()).andReturn(resourceInstance).anyTimes(); - - expect(readRequest.getURI()).andReturn(clusterName).anyTimes(); - Map fields = new HashMap<>(); - expect(readRequest.getFields()).andReturn(fields).anyTimes(); - List queryResults = new ArrayList<>(); - queryResults.add(new HostInfoSummaryDTO("redhat6", 10)); - queryResults.add(new HostInfoSummaryDTO("debian7", 20)); - expect(hostInfoSummaryDAO.findHostInfoSummary("c1")).andReturn(queryResults).anyTimes(); - expect(hostInfoSummaryDAO.findHostInfoSummary(null)).andReturn(queryResults).anyTimes(); - - replay(publisher, hostInfoSummaryDAO, readRequest); - - ViewRegistry.initInstance(new ViewRegistry(publisher)); - RequestHandler requestHandler = new ReadHandler(); - Result result = requestHandler.handleRequest(readRequest); - - Assert.assertEquals(ResultStatus.STATUS.OK, result.getStatus().getStatus()); - - verify(publisher, hostInfoSummaryDAO, readRequest); - } - - /** - * - */ - private class MockModule implements Module { - /** - * - */ - @Override - public void configure(Binder binder) { - binder.bind(HostInfoSummaryDAO.class).toInstance(hostInfoSummaryDAO); - } - } -} diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java index e68ff2eb03d..69721cd4ba6 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java @@ -42,6 +42,7 @@ import javax.persistence.EntityManager; + import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.HostNotFoundException; import org.apache.ambari.server.actionmanager.ActionDBAccessor; @@ -62,6 +63,9 @@ import org.apache.ambari.server.controller.utilities.PredicateBuilder; import org.apache.ambari.server.controller.utilities.PropertyHelper; import org.apache.ambari.server.orm.DBAccessor; +import org.apache.ambari.server.orm.InMemoryDefaultTestModule; +import org.apache.ambari.server.orm.dao.HostInfoSummaryDAO; +import org.apache.ambari.server.orm.dao.HostInfoSummaryDTO; import org.apache.ambari.server.scheduler.ExecutionScheduler; import org.apache.ambari.server.security.TestAuthenticationFactory; import org.apache.ambari.server.security.authorization.AuthorizationException; @@ -89,8 +93,11 @@ import com.google.gson.Gson; import com.google.inject.AbstractModule; +import com.google.inject.Binder; import com.google.inject.Guice; import com.google.inject.Injector; +import com.google.inject.Module; +import com.google.inject.util.Modules; /** * HostResourceProvider tests. @@ -1283,6 +1290,61 @@ public void testGetHosts___OR_Predicate_HostNotFoundException() throws Exception verifyAll(); } + @Test + public void testHostsSummary() throws Exception { + Injector injector = createInjector(); + AmbariManagementController managementController = injector.getInstance(AmbariManagementController.class); + ResourceProvider hostResourceProvider = getHostProvider(injector); + + injector = Guice.createInjector(Modules.override( + new InMemoryDefaultTestModule()).with(new HostResourceProviderTest.MockModule())); + HostInfoSummaryDAO hostInfoSummaryDAO = injector.getInstance(HostInfoSummaryDAO.class); + List queryResults = new ArrayList<>(); + queryResults.add(new HostInfoSummaryDTO("redhat6", 10)); + queryResults.add(new HostInfoSummaryDTO("debian7", 20)); + expect(hostInfoSummaryDAO.findHostInfoSummary("c1")).andReturn(queryResults).anyTimes(); + + Set propertyIds = new HashSet<>(); + + propertyIds.add(HostResourceProvider.HOST_CLUSTER_NAME_PROPERTY_ID); + propertyIds.add(HostResourceProvider.HOST_SUMMARY_PROPERTY_ID); + + Predicate predicate = buildPredicate("c1", null); + Request request = PropertyHelper.getReadRequest(propertyIds); + + replayAll(); + + SecurityContextHolder.getContext().setAuthentication(TestAuthenticationFactory.createAdministrator()); + + Set resources = hostResourceProvider.getResources(request, predicate); + + Assert.assertEquals(1, resources.size()); + for (Resource resource : resources) { + Map propertyMap = (Map)resource.getPropertiesMap().values().iterator().next(); + String clusterName = (String)(propertyMap.get("cluster_name")); + Assert.assertEquals("c1", clusterName); + Map summaryMap = (Map)((List)propertyMap.get("summary")).get(0); + Assert.assertEquals(1, summaryMap.size()); + List> osSummary = (List>)summaryMap.get("operating_systems"); + Assert.assertEquals(10, (int)osSummary.get(0).get("redhat6")); + Assert.assertEquals(20, (int)osSummary.get(1).get("debian7")); + } + + verifyAll(); + } + + /** + * A class to generate in-memory singleton instance for object + */ + private class MockModule implements Module { + + @Override + public void configure(Binder binder) { + binder.bind(HostInfoSummaryDAO.class).toInstance(createStrictMock(HostInfoSummaryDAO.class)); + // We can add more + } + } + public static void createHosts(AmbariManagementController controller, Set requests) throws AmbariException, AuthorizationException { HostResourceProvider provider = getHostProvider(controller); Set> properties = new HashSet<>();