/** * 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.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.AlertResourceProvider; import org.apache.ambari.server.controller.internal.ResourceImpl; import org.apache.ambari.server.controller.spi.Resource; import org.apache.ambari.server.state.AlertState; import org.apache.ambari.server.state.MaintenanceState; /** * The {@link AlertSummaryRenderer} is used to format the results of queries to * the alerts endpoint. Each item returned from the query represents an * individual current alert which is then aggregated into a summary structure * based on the alert state. * <p/> * The finalized structure is: * * <pre> * { * "href" : "http://localhost:8080/api/v1/clusters/c1/alerts?format=summary", * "alerts_summary" : { * "CRITICAL" : { * "count" : 3, * "maintenance_count" : 1, * "original_timestamp" : 1415372828182 * }, * "OK" : { * "count" : 37, * "maintenance_count" : 0, * "original_timestamp" : 1415375364937 * }, * "UNKNOWN" : { * "count" : 1, * "maintenance_count" : 0, * "original_timestamp" : 1415372632261 * }, * "WARN" : { * "count" : 0, * "maintenance_count" : 0, * "original_timestamp" : 0 * } * } * } * </pre> * <p/> * The nature of a {@link Renderer} is that it manipulates the dataset returned * by a query. In the case of alert data, the query could potentially return * thousands of results if there are thousands of nodes in the cluster. This * could present a performance issue that can only be addressed by altering the * incoming query and modifying it to instruct the backend to return a JPA SUM * instead of a collection of entities. */ public class AlertSummaryRenderer extends BaseRenderer implements Renderer { /** * {@inheritDoc} */ @Override public TreeNode<Set<String>> finalizeProperties( TreeNode<QueryInfo> queryTree, boolean isCollection) { QueryInfo queryInfo = queryTree.getObject(); TreeNode<Set<String>> resultTree = new TreeNodeImpl<>( null, queryInfo.getProperties(), queryTree.getName()); copyPropertiesToResult(queryTree, resultTree); boolean addKeysToEmptyResource = true; if (!isCollection && isRequestWithNoProperties(queryTree)) { addSubResources(queryTree, resultTree); addKeysToEmptyResource = false; } ensureRequiredProperties(resultTree, addKeysToEmptyResource); // ensure that state and original_timestamp are on the request since these // are required by the finalization process of this renderer Set<String> properties = resultTree.getObject(); addRequiredAlertProperties(properties); return resultTree; } /** * {@inheritDoc} */ @Override public ResultPostProcessor getResultPostProcessor(Request request) { // simply return the native rendering return new ResultPostProcessorImpl(request); } /** * {@inheritDoc} * <p/> * This will iterate over all of the nodes in the result tree and combine * their {@link AlertResourceProvider#ALERT_STATE} into a single summary * structure. */ @Override public Result finalizeResult(Result queryResult) { TreeNode<Resource> resultTree = queryResult.getResultTree(); AlertStateSummary alertSummary = new AlertStateSummary(); // iterate over all returned flattened alerts and build the summary info for (TreeNode<Resource> node : resultTree.getChildren()) { Resource resource = node.getObject(); AlertState state = (AlertState) resource.getPropertyValue(AlertResourceProvider.ALERT_STATE); Long originalTimestampObject = (Long) resource.getPropertyValue(AlertResourceProvider.ALERT_ORIGINAL_TIMESTAMP); MaintenanceState maintenanceState = (MaintenanceState) resource.getPropertyValue(AlertResourceProvider.ALERT_MAINTENANCE_STATE); // NPE sanity if (null == state) { state = AlertState.UNKNOWN; } // NPE sanity long originalTimestamp = 0; if (null != originalTimestampObject) { originalTimestamp = originalTimestampObject.longValue(); } // NPE sanity boolean isMaintenanceModeEnabled = false; if (null != maintenanceState && maintenanceState != MaintenanceState.OFF) { isMaintenanceModeEnabled = true; } final AlertStateValues alertStateValues; switch (state) { case CRITICAL: { alertStateValues = alertSummary.Critical; break; } case OK: { alertStateValues = alertSummary.Ok; break; } case WARNING: { alertStateValues = alertSummary.Warning; break; } default: case UNKNOWN: { alertStateValues = alertSummary.Unknown; break; } } if (isMaintenanceModeEnabled) { alertStateValues.MaintenanceCount++; } else { alertStateValues.Count++; } if (originalTimestamp > alertStateValues.Timestamp) { alertStateValues.Timestamp = originalTimestamp; } } Result summary = new ResultImpl(true); Resource resource = new ResourceImpl(Resource.Type.Alert); TreeNode<Resource> summaryTree = summary.getResultTree(); summaryTree.addChild(resource, "alerts_summary"); resource.setProperty("alerts_summary", alertSummary); return summary; } /** * Adds properties to the backend request that are required by this renderer. * This method currently adds {@link AlertResourceProvider#ALERT_STATE} and * {@link AlertResourceProvider#ALERT_ORIGINAL_TIMESTAMP}. * * @param properties * the properties collection to add to. */ protected void addRequiredAlertProperties(Set<String> properties) { properties.add(AlertResourceProvider.ALERT_STATE); properties.add(AlertResourceProvider.ALERT_ORIGINAL_TIMESTAMP); properties.add(AlertResourceProvider.ALERT_MAINTENANCE_STATE); } }