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..0e307b23825 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 @@ -121,4 +121,5 @@ private void addFieldsToQuery(Request request, Query query) { query.addProperty(propertyId, entry.getValue()); } } + } diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostSummaryRenderer.java b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostSummaryRenderer.java new file mode 100644 index 00000000000..3d6b843c082 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostSummaryRenderer.java @@ -0,0 +1,99 @@ +/* + * 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.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.ambari.server.api.services.Result; +import org.apache.ambari.server.api.services.ResultImpl; +import org.apache.ambari.server.api.util.TreeNode; +import org.apache.ambari.server.controller.internal.HostResourceProvider; +import org.apache.ambari.server.controller.internal.ResourceImpl; +import org.apache.ambari.server.controller.spi.Resource; +import org.apache.commons.lang.StringUtils; + +/** + * HostSummaryRenderere + * + * This renderer is to summarize the properties of all hosts within a cluster and returns + * a list of proerpties summary information: i.e + * + * summary:[ + * { + * "operating_systems" : { + * "centos6" : 5, + * "centos7" : 10 + * } + * } + * ] + * + */ +public class HostSummaryRenderer extends DefaultRenderer { + + // A list of host properties to be summarized + enum HostSummaryProperties { + OPERATINGSYSTEMS("operating_systems"); + + private final String property; + HostSummaryProperties(String property) { + this.property = property; + } + public String getProperty() { + return property; + } + } + + @Override + public Result finalizeResult(Result queryResult) { + TreeNode queryResultTree = queryResult.getResultTree(); + // Iterate over all returned flattened hosts and build the summary info + List summary = new ArrayList<>(); + // Build all summary info + buildFinalizedSummary(queryResultTree, summary); + // Create finalized result + return buildFinalizedResult(summary); + } + + private void buildFinalizedSummary(TreeNode queryResultTree, List summary) { + // Build osSummary info at this time + Map> osSummary = new HashMap<>(); + summary.add(osSummary); + Map osTypeCount = new HashMap<>(); + osSummary.put(HostSummaryProperties.OPERATINGSYSTEMS.getProperty(), osTypeCount); + for (TreeNode node : queryResultTree.getChildren()) { + Resource resource = node.getObject(); + String osType = (String) resource.getPropertyValue(HostResourceProvider.HOST_OS_TYPE_PROPERTY_ID); + if (StringUtils.isNotBlank(osType)) { + osTypeCount.put(osType, osTypeCount.getOrDefault(osTypeCount, 0) + 1); + } + } + } + + private Result buildFinalizedResult(List summary) { + Result result = new ResultImpl(true); + Resource resource = new ResourceImpl(Resource.Type.Host); + TreeNode summaryTree = result.getResultTree(); + summaryTree.addChild(resource, HostResourceProvider.SUMMARY_PROPERTY_ID); + resource.setProperty(HostResourceProvider.SUMMARY_PROPERTY_ID, summary); + return result; + } +} 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..35e237cc78f 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.HostSummaryRenderer; +import org.apache.ambari.server.api.query.render.Renderer; import org.apache.ambari.server.controller.spi.Resource; @@ -40,4 +42,13 @@ public String getPluralName() { public String getSingularName() { return "host"; } + + @Override + public Renderer getRenderer(String name) { + if (name.equals("summary")) { + return new HostSummaryRenderer(); + } + return super.getRenderer(name); + } + } 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..46ca04add1c 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.HostSummaryRenderer; +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,14 @@ public Collection getDeleteDirectives() { return Collections.singleton(Request.DIRECTIVE_DRY_RUN); } + @Override + public Renderer getRenderer(String name) { + if (name.equals("summary")) { + return new HostSummaryRenderer(); + } + return super.getRenderer(name); + } + @Override public Set getSubResourceDefinitions() { Set subs = new HashSet<>(); diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostService.java index 58d1d1d0335..aa9089ec785 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostService.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostService.java @@ -28,6 +28,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; @@ -133,7 +134,8 @@ public Response getHost(String body, @Context HttpHeaders headers, @Context UriI @ApiResponse(code = HttpStatus.SC_FORBIDDEN, message = MSG_PERMISSION_DENIED), @ApiResponse(code = HttpStatus.SC_INTERNAL_SERVER_ERROR, message = MSG_SERVER_ERROR), }) - public Response getHosts(String body, @Context HttpHeaders headers, @Context UriInfo ui) { + public Response getHosts(String body, @Context HttpHeaders headers, @Context UriInfo ui, + @ApiParam(value = "summary", required = false) @QueryParam("format") String format) { return handleRequest(headers, body, ui, Request.Type.GET, createHostResource(m_clusterName, null)); } 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 1dbe9032f6d..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 @@ -132,6 +132,8 @@ protected ResourceProvider createResourceProvider(Resource.Type type) { return new ArtifactResourceProvider(managementController); case RemoteCluster: return new RemoteClusterResourceProvider(); + case Host: + return new HostResourceProvider(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/HostResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostResourceProvider.java index 7ecbfdc13bc..79f4233ebce 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 @@ -116,6 +116,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 +140,7 @@ 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 BLUEPRINT_PROPERTY_ID = "blueprint"; public static final String HOST_GROUP_PROPERTY_ID = "host_group"; @@ -154,6 +155,7 @@ public class HostResourceProvider extends AbstractControllerResourceProvider { public static Map keyPropertyIds = ImmutableMap.builder() .put(Resource.Type.Host, HOST_HOST_NAME_PROPERTY_ID) .put(Resource.Type.Cluster, HOST_CLUSTER_NAME_PROPERTY_ID) + .put(Resource.Type.HostComponent, HOST_OS_TYPE_PROPERTY_ID) .build(); /** @@ -195,7 +197,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 +216,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 +240,11 @@ public Void invoke() throws AmbariException, AuthorizationException { @Override protected Set getResourcesAuthorized(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { + return getHostResource(request, predicate); + } + + private Set getHostResource(Request request, Predicate predicate) + throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { final Set requests = new HashSet<>();