| /* |
| * 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.alerts; |
| |
| import java.text.MessageFormat; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.TreeMap; |
| |
| import org.apache.ambari.server.AmbariException; |
| import org.apache.ambari.server.api.services.AmbariMetaInfo; |
| import org.apache.ambari.server.orm.entities.AlertDefinitionEntity; |
| import org.apache.ambari.server.orm.entities.RepositoryVersionEntity; |
| import org.apache.ambari.server.orm.entities.UpgradeEntity; |
| import org.apache.ambari.server.state.Alert; |
| import org.apache.ambari.server.state.AlertState; |
| import org.apache.ambari.server.state.Cluster; |
| import org.apache.ambari.server.state.ComponentInfo; |
| import org.apache.ambari.server.state.Host; |
| import org.apache.ambari.server.state.Service; |
| import org.apache.ambari.server.state.ServiceComponent; |
| import org.apache.ambari.server.state.ServiceComponentHost; |
| import org.apache.ambari.server.state.StackId; |
| import org.apache.ambari.server.state.stack.upgrade.Direction; |
| import org.apache.commons.lang.StringUtils; |
| |
| import com.google.inject.Inject; |
| |
| /** |
| * The {@link ComponentVersionAlertRunnable} is used to determine if the |
| * reported version of host components match what is expected. If there is a |
| * mismatch, then an alert will be triggered indicating which components are in |
| * need of attention. |
| * <p/> |
| * This alert will not run during upgrades or when the cluster is still being |
| * provisioned. |
| */ |
| public class ComponentVersionAlertRunnable extends AlertRunnable { |
| |
| /** |
| * The message for the alert when all components are reporting correct |
| * versions. |
| */ |
| private static final String ALL_COMPONENTS_CORRECT_MSG = "All components are reporting their expected versions."; |
| |
| /** |
| * The message for the alert when there is an upgrade in progress. |
| */ |
| private static final String UPGRADE_IN_PROGRESS_MSG = "This alert will be suspended while the {0} is in progress."; |
| |
| /** |
| * The unknown component error message. |
| */ |
| private static final String UNKNOWN_COMPONENT_MSG_TEMPLATE = "Unable to retrieve component information for {0}/{1}"; |
| |
| /** |
| * The version mismatch message. |
| */ |
| private static final String MISMATCHED_VERSIONS_MSG = "The following components are reporting unexpected versions: "; |
| |
| @Inject |
| private AmbariMetaInfo m_metaInfo; |
| |
| /** |
| * Constructor. |
| * |
| * @param definitionName |
| */ |
| public ComponentVersionAlertRunnable(String definitionName) { |
| super(definitionName); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| List<Alert> execute(Cluster cluster, AlertDefinitionEntity myDefinition) throws AmbariException { |
| // if there is an upgrade in progress, then skip running this alert |
| UpgradeEntity upgrade = cluster.getUpgradeInProgress(); |
| if (null != upgrade) { |
| Direction direction = upgrade.getDirection(); |
| String message = MessageFormat.format(UPGRADE_IN_PROGRESS_MSG, direction.getText(false)); |
| |
| return Collections.singletonList( |
| buildAlert(cluster, myDefinition, AlertState.SKIPPED, message)); |
| } |
| |
| TreeMap<Host, Set<ServiceComponentHost>> versionMismatches = new TreeMap<>(); |
| Collection<Host> hosts = cluster.getHosts(); |
| |
| for (Host host : hosts) { |
| List<ServiceComponentHost> hostComponents = cluster.getServiceComponentHosts(host.getHostName()); |
| for (ServiceComponentHost hostComponent : hostComponents) { |
| Service service = cluster.getService(hostComponent.getServiceName()); |
| ServiceComponent serviceComponent = service.getServiceComponent(hostComponent.getServiceComponentName()); |
| |
| RepositoryVersionEntity desiredRepositoryVersion = service.getDesiredRepositoryVersion(); |
| StackId desiredStackId = serviceComponent.getDesiredStackId(); |
| String desiredVersion = desiredRepositoryVersion.getVersion(); |
| |
| final ComponentInfo componentInfo; |
| try { |
| componentInfo = m_metaInfo.getComponent(desiredStackId.getStackName(), |
| desiredStackId.getStackVersion(), hostComponent.getServiceName(), |
| hostComponent.getServiceComponentName()); |
| } catch (AmbariException ambariException) { |
| // throw an UNKNOWN response if we can't load component info |
| String message = MessageFormat.format(UNKNOWN_COMPONENT_MSG_TEMPLATE, |
| hostComponent.getServiceName(), hostComponent.getServiceComponentName()); |
| |
| return Collections.singletonList( |
| buildAlert(cluster, myDefinition, AlertState.UNKNOWN, message)); |
| } |
| |
| // skip components that don't advertise a version |
| if (!componentInfo.isVersionAdvertised()) { |
| continue; |
| } |
| |
| String version = hostComponent.getVersion(); |
| if (!StringUtils.equals(version, desiredVersion)) { |
| Set<ServiceComponentHost> mismatchedComponents = versionMismatches.get(host); |
| if (null == mismatchedComponents) { |
| mismatchedComponents = new HashSet<>(); |
| versionMismatches.put(host, mismatchedComponents); |
| } |
| |
| mismatchedComponents.add(hostComponent); |
| } |
| } |
| } |
| |
| AlertState alertState = AlertState.OK; |
| String alertText = ALL_COMPONENTS_CORRECT_MSG; |
| |
| // if there are any components reporting the wrong version, fire off a warning |
| if (!versionMismatches.isEmpty()) { |
| StringBuilder buffer = new StringBuilder(MISMATCHED_VERSIONS_MSG); |
| buffer.append(System.lineSeparator()); |
| |
| for (Host host : versionMismatches.keySet()) { |
| buffer.append(" ").append(host.getHostName()); |
| buffer.append(System.lineSeparator()); |
| for (ServiceComponentHost hostComponent : versionMismatches.get(host)) { |
| buffer.append(" ").append(hostComponent.getServiceComponentName()).append(": ").append( |
| hostComponent.getVersion()).append(System.lineSeparator()); |
| } |
| } |
| |
| alertText = buffer.toString(); |
| alertState = AlertState.WARNING; |
| } |
| |
| return Collections.singletonList(buildAlert(cluster, myDefinition, alertState, alertText)); |
| } |
| } |