content: /*
   * 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.Map;
  import java.util.Set;
  
  import org.apache.ambari.server.AmbariException;
  import org.apache.ambari.server.StaticallyInject;
  import org.apache.ambari.server.controller.spi.Predicate;
  import org.apache.ambari.server.controller.spi.PropertyProvider;
  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.orm.dao.AlertHostSummaryDTO;
  import org.apache.ambari.server.orm.dao.AlertSummaryDTO;
  import org.apache.ambari.server.orm.dao.AlertsDAO;
  import org.apache.ambari.server.state.AlertState;
  import org.apache.ambari.server.state.Cluster;
  import org.apache.ambari.server.state.Clusters;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.google.common.collect.ImmutableSet;
  import com.google.inject.Inject;
  import com.google.inject.Provider;
  
  /**
   * Property provider that adds alert summary information to endpoints.
   */
  @StaticallyInject
  public class AlertSummaryPropertyProvider extends BaseProvider implements PropertyProvider {
  
    private final static Logger LOG = LoggerFactory.getLogger(AlertSummaryPropertyProvider.class);
  
    /**
     * The property ID for a summary of all cluster-wide alerts.
     */
    private final static String ALERTS_SUMMARY = "alerts_summary";
  
    /**
     * The property ID for a summary of all cluster-wide alerts that have a host
     * associated with them.
     */
    private final static String ALERTS_SUMMARY_HOSTS = "alerts_summary_hosts";
  
    @Inject
    private static Provider<Clusters> s_clusters = null;
  
    @Inject
    private static AlertsDAO s_dao = null;
  
    private Resource.Type m_resourceType = null;
    private String m_clusterPropertyId = null;
    private String m_typeIdPropertyId = null;
  
    /**
     * Constructor.
     *
     * @param type
     * @param clusterPropertyId
     * @param typeIdPropertyId
     */
    AlertSummaryPropertyProvider(Resource.Type type,
        String clusterPropertyId, String typeIdPropertyId) {
      super(ImmutableSet.of(ALERTS_SUMMARY, ALERTS_SUMMARY_HOSTS));
      m_resourceType = type;
      m_clusterPropertyId = clusterPropertyId;
      m_typeIdPropertyId = typeIdPropertyId;
    }
  
    @Override
    public Set<Resource> populateResources(Set<Resource> resources,
        Request request, Predicate predicate) throws SystemException {
      Set<String> propertyIds = getRequestPropertyIds(request, predicate);
  
      try {
        // Optimization:
        // Some information can be determined more efficiently when requested in bulk 
        // for an entire cluster at once. 
        // For Example:
        //   (1) Cluster level alert-status counts
        //   (2) Per host alert-status counts
        // These can be determined in 1 SQL call per cluster, and results used multiple times.
        Map<Long, Map<String, AlertSummaryDTO>> perHostSummaryMap = new HashMap<>();
        Map<Long, AlertHostSummaryDTO> hostsSummaryMap = new HashMap<>();
        Map<String, Cluster> resourcesClusterMap = new HashMap<>();
        for (Resource res : resources) {
          String clusterName = (String) res.getPropertyValue(m_clusterPropertyId);
          if (clusterName == null || resourcesClusterMap.containsKey(clusterName)) {
            continue;
          }
          Cluster cluster = s_clusters.get().getCluster(clusterName);
          resourcesClusterMap.put(clusterName, cluster);
        }
        for (Cluster cluster : resourcesClusterMap.values()) {
          long clusterId = cluster.getClusterId();
          switch (m_resourceType.getInternalType()) {
            case Cluster:
              // only make the calculation if asked
              if (BaseProvider.isPropertyRequested(ALERTS_SUMMARY_HOSTS, propertyIds)) {
                hostsSummaryMap.put(clusterId, s_dao.findCurrentHostCounts(clusterId));
              }
              break;
            case Host:
              if (resources.size() > 1) {
                // More efficient to get information for all hosts in 1 call
                Map<String, AlertSummaryDTO> perHostCounts = s_dao.findCurrentPerHostCounts(clusterId);
                perHostSummaryMap.put(clusterId, perHostCounts);
              }
              break;
            default:
              break;
          }
        }
  
        for (Resource res : resources) {
          populateResource(res, propertyIds, perHostSummaryMap, hostsSummaryMap);
        }
      } catch (AmbariException e) {
        LOG.error("Could not load built-in alerts - Executor exception ({})",
            e.getMessage());
      }
      return resources;
    }
  
    private void populateResource(Resource resource, Set<String> requestedIds, Map<Long, Map<String, 
        AlertSummaryDTO>> perHostSummaryMap, Map<Long, AlertHostSummaryDTO> hostsSummaryMap) throws AmbariException {
  
      AlertSummaryDTO summary = null;
      AlertHostSummaryDTO hostSummary = null;
  
      String clusterName = (String) resource.getPropertyValue(m_clusterPropertyId);
  
      if (null == clusterName) {
        return;
      }
  
      String typeId = null == m_typeIdPropertyId ? null : (String) resource.getPropertyValue(m_typeIdPropertyId);
      Cluster cluster = s_clusters.get().getCluster(clusterName);
  
      switch (m_resourceType.getInternalType()) {
        case Cluster:
          long clusterId = cluster.getClusterId();
  
          // only make the calculation if asked
          if (BaseProvider.isPropertyRequested(ALERTS_SUMMARY, requestedIds)) {
            summary = s_dao.findCurrentCounts(cluster.getClusterId(), null, null);
          }
  
          // only make the calculation if asked
          if (BaseProvider.isPropertyRequested(ALERTS_SUMMARY_HOSTS,
              requestedIds)) {
            if (hostsSummaryMap.containsKey(cluster.getClusterId())) {
              hostSummary = hostsSummaryMap.get(cluster.getClusterId());
            } else {
              hostSummary = s_dao.findCurrentHostCounts(clusterId);
            }
          }
  
          break;
        case Service:
          summary = s_dao.findCurrentCounts(cluster.getClusterId(), typeId, null);
          break;
        case Host:
        if (perHostSummaryMap.containsKey(cluster.getClusterId()) && 
            perHostSummaryMap.get(cluster.getClusterId()).containsKey(typeId)) {
          summary = perHostSummaryMap.get(cluster.getClusterId()).get(typeId);
        } else {
          summary = s_dao.findCurrentCounts(cluster.getClusterId(), null, typeId);
        }
          break;
        default:
          break;
      }
  
      // all alerts in the cluster, in summary count form
      if (null != summary) {
        Map<String, Integer> map = new HashMap<>();
        map.put(AlertState.OK.name(), Integer.valueOf(summary.getOkCount()));
        map.put(AlertState.WARNING.name(), Integer.valueOf(summary.getWarningCount()));
        map.put(AlertState.CRITICAL.name(), Integer.valueOf(summary.getCriticalCount()));
        map.put(AlertState.UNKNOWN.name(), Integer.valueOf(summary.getUnknownCount()));
        map.put("MAINTENANCE", Integer.valueOf(summary.getMaintenanceCount()));
        setResourceProperty(resource, ALERTS_SUMMARY, map, requestedIds);
      }
  
      // the summary of hosts with warning or critical alerts
      if (null != hostSummary) {
        Map<AlertState, Integer> map = new HashMap<>();
        map.put(AlertState.OK, Integer.valueOf(hostSummary.getOkCount()));
        map.put(AlertState.WARNING, Integer.valueOf(hostSummary.getWarningCount()));
        map.put(AlertState.CRITICAL, Integer.valueOf(hostSummary.getCriticalCount()));
        map.put(AlertState.UNKNOWN, Integer.valueOf(hostSummary.getUnknownCount()));
        setResourceProperty(resource, ALERTS_SUMMARY_HOSTS, map, requestedIds);
      }
  
    }
  
    @Override
    public Set<String> checkPropertyIds(Set<String> propertyIds) {
      Set<String> rejects = new HashSet<>();
  
      for (String id : propertyIds) {
        if (!id.startsWith(ALERTS_SUMMARY)) {
          rejects.add(id);
        }
      }
  
      return rejects;
    }
  
  }
  