/** * 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.events.listeners.upgrade; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.EagerSingleton; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.events.HostComponentVersionAdvertisedEvent; import org.apache.ambari.server.events.publishers.VersionEventPublisher; import org.apache.ambari.server.orm.dao.RepositoryVersionDAO; import org.apache.ambari.server.orm.entities.RepositoryVersionEntity; import org.apache.ambari.server.state.Cluster; import org.apache.ambari.server.state.ComponentInfo; import org.apache.ambari.server.state.ServiceComponent; import org.apache.ambari.server.state.ServiceComponentHost; import org.apache.ambari.server.state.State; import org.apache.ambari.server.state.UpgradeState; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; /** * The {@link StackVersionListener} class handles the propagation of versions * advertised by the {@link org.apache.ambari.server.state.ServiceComponentHost} * that bubble up to the * {@link org.apache.ambari.server.orm.entities.HostVersionEntity} and * eventually the * {@link org.apache.ambari.server.orm.entities.ClusterVersionEntity} */ @Singleton @EagerSingleton public class StackVersionListener { /** * Logger. */ private final static Logger LOG = LoggerFactory.getLogger(StackVersionListener.class); public static final String UNKNOWN_VERSION = State.UNKNOWN.toString(); @Inject private RepositoryVersionDAO repositoryVersionDAO; @Inject Provider<AmbariMetaInfo> ambariMetaInfo; /** * Constructor. * * @param eventPublisher the publisher */ @Inject public StackVersionListener(VersionEventPublisher eventPublisher) { eventPublisher.register(this); } @Subscribe public void onAmbariEvent(HostComponentVersionAdvertisedEvent event) { LOG.debug("Received event {}", event); Cluster cluster = event.getCluster(); ServiceComponentHost sch = event.getServiceComponentHost(); String newVersion = event.getVersion(); if (StringUtils.isEmpty(newVersion)) { return; } // if the cluster is upgrading, there's no need to update the repo version - // it better be right if (null != event.getRepositoryVersionId() && null == cluster.getUpgradeInProgress()) { // !!! make sure the repo_version record actually has the same version. // This is NOT true when installing a cluster using a public repo where the // exact version is not known in advance. RepositoryVersionEntity rve = repositoryVersionDAO.findByPK(event.getRepositoryVersionId()); if (null != rve) { String currentRepoVersion = rve.getVersion(); if (!StringUtils.equals(currentRepoVersion, newVersion)) { rve.setVersion(newVersion); repositoryVersionDAO.merge(rve); } } } // Update host component version value if needed try { AmbariMetaInfo metaInfo = ambariMetaInfo.get(); ComponentInfo componentInfo = metaInfo.getComponent(cluster.getDesiredStackVersion().getStackName(), cluster.getDesiredStackVersion().getStackVersion(), sch.getServiceName(), sch.getServiceComponentName()); ServiceComponent sc = cluster.getService(sch.getServiceName()).getServiceComponent(sch.getServiceComponentName()); if (componentInfo.isVersionAdvertised() && StringUtils.isNotBlank(newVersion) && !UNKNOWN_VERSION.equalsIgnoreCase(newVersion)) { processComponentAdvertisedVersion(cluster, sch, newVersion, sc); } else if(!sc.isVersionAdvertised() && StringUtils.isNotBlank(newVersion) && !UNKNOWN_VERSION.equalsIgnoreCase(newVersion)) { LOG.debug("ServiceComponent {} doesn't advertise version, " + "however ServiceHostComponent {} on host {} advertised version as {}. Skipping version update", sc.getName(), sch.getServiceComponentName(), sch.getHostName(), newVersion); } else { if (UNKNOWN_VERSION.equals(sc.getDesiredVersion())) { processUnknownDesiredVersion(cluster, sc, sch, newVersion); } else { processComponentAdvertisedVersion(cluster, sch, newVersion, sc); } } } catch (Exception e) { LOG.error( "Unable to propagate version for ServiceHostComponent on component: {}, host: {}. Error: {}", sch.getServiceComponentName(), sch.getHostName(), e.getMessage()); } } /** * Update host component version * or * Bootstrap cluster/repo version when version is reported for the first time * @param cluster target cluster * @param sch target host component * @param newVersion advertised version * @param sc target service component * @throws AmbariException */ private void processComponentAdvertisedVersion(Cluster cluster, ServiceComponentHost sch, String newVersion, ServiceComponent sc) throws AmbariException { if (StringUtils.isBlank(newVersion)) { return; } String previousVersion = sch.getVersion(); if (previousVersion == null || UNKNOWN_VERSION.equalsIgnoreCase(previousVersion)) { // value may be "UNKNOWN" when upgrading from older Ambari versions // or if host component reports it's version for the first time sch.setUpgradeState(UpgradeState.NONE); sch.setVersion(newVersion); bootstrapVersion(cluster, sch); } else if (!StringUtils.equals(previousVersion, newVersion)) { processComponentVersionChange(cluster, sc, sch, newVersion); } sc.updateRepositoryState(newVersion); } /** * Bootstrap cluster/repo version when version is reported for the first time * @param cluster target cluster * @param sch target host component * @throws AmbariException */ private void bootstrapVersion(Cluster cluster, ServiceComponentHost sch) throws AmbariException { RepositoryVersionEntity repoVersion = sch.recalculateHostVersionState(); if (null != repoVersion) { cluster.recalculateClusterVersionState(repoVersion); } } /** * Possible situation after upgrade from older Ambari version. Just use * reported component version as desired version * @param cluster target cluster * @param sc target service component * @param sch target host component * @param newVersion advertised version */ private void processUnknownDesiredVersion(Cluster cluster, ServiceComponent sc, ServiceComponentHost sch, String newVersion) throws AmbariException { sc.setDesiredVersion(newVersion); sch.setUpgradeState(UpgradeState.NONE); sch.setVersion(newVersion); bootstrapVersion(cluster, sch); } /** * Focuses on cases when host component version really changed * @param cluster target cluster * @param sc target service component * @param sch target host component * @param newVersion advertised version */ private void processComponentVersionChange(Cluster cluster, ServiceComponent sc, ServiceComponentHost sch, String newVersion) { String desiredVersion = sc.getDesiredVersion(); UpgradeState upgradeState = sch.getUpgradeState(); if (upgradeState == UpgradeState.IN_PROGRESS) { // Component status update is received during upgrade process if (desiredVersion.equals(newVersion)) { sch.setUpgradeState(UpgradeState.COMPLETE); // Component upgrade confirmed sch.setStackVersion(cluster.getDesiredStackVersion()); } else { // Unexpected (wrong) version received // Even during failed upgrade, we should not receive wrong version // That's why mark as VERSION_MISMATCH sch.setUpgradeState(UpgradeState.VERSION_MISMATCH); } } else if (upgradeState == UpgradeState.VERSION_MISMATCH && desiredVersion.equals(newVersion)) { if (cluster.getUpgradeInProgress() != null) { sch.setUpgradeState(UpgradeState.COMPLETE); } else { sch.setUpgradeState(UpgradeState.NONE); } } else { // No upgrade in progress, unexpected version change sch.setUpgradeState(UpgradeState.VERSION_MISMATCH); } sch.setVersion(newVersion); } }