/** * 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 java.util.List; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.EagerSingleton; import org.apache.ambari.server.actionmanager.HostRoleStatus; import org.apache.ambari.server.bootstrap.DistributeRepositoriesStructuredOutput; import org.apache.ambari.server.events.ActionFinalReportReceivedEvent; import org.apache.ambari.server.events.publishers.AmbariEventPublisher; import org.apache.ambari.server.orm.dao.HostVersionDAO; import org.apache.ambari.server.orm.dao.RepositoryVersionDAO; import org.apache.ambari.server.orm.entities.HostVersionEntity; import org.apache.ambari.server.orm.entities.RepositoryVersionEntity; import org.apache.ambari.server.state.Cluster; import org.apache.ambari.server.state.Clusters; import org.apache.ambari.server.state.RepositoryVersionState; import org.apache.ambari.server.state.StackId; import org.apache.ambari.server.utils.StageUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.eventbus.Subscribe; import com.google.gson.JsonSyntaxException; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; /** * The {@link org.apache.ambari.server.events.listeners.upgrade.DistributeRepositoriesActionListener} class * handles {@link org.apache.ambari.server.events.ActionFinalReportReceivedEvent} * for "Distribute repositories/install packages" action. * It processes command reports and and updates host stack version state acordingly. */ @Singleton @EagerSingleton public class DistributeRepositoriesActionListener { /** * Logger. */ private static final Logger LOG = LoggerFactory.getLogger(DistributeRepositoriesActionListener.class); public static final String INSTALL_PACKAGES = "install_packages"; @Inject private Provider<HostVersionDAO> hostVersionDAO; @Inject private Provider<Clusters> clusters; @Inject private RepositoryVersionDAO repoVersionDAO; /** * Constructor. * * @param publisher */ @Inject public DistributeRepositoriesActionListener(AmbariEventPublisher publisher) { publisher.register(this); } @Subscribe public void onActionFinished(ActionFinalReportReceivedEvent event) { // Check if it is "Distribute repositories/install packages" action. if (! event.getRole().equals(INSTALL_PACKAGES)) { return; } if (LOG.isDebugEnabled()) { LOG.debug(event.toString()); } RepositoryVersionState newHostState = RepositoryVersionState.INSTALL_FAILED; Long clusterId = event.getClusterId(); if (clusterId == null) { LOG.error("Distribute Repositories expected a cluster Id for host " + event.getHostname()); return; } String repositoryVersion = null; if (event.getCommandReport() == null) { LOG.error( "Command report is null, will set all INSTALLING versions for host {} to INSTALL_FAILED.", event.getHostname()); } else if (!event.getCommandReport().getStatus().equals(HostRoleStatus.COMPLETED.toString())) { LOG.warn( "Distribute repositories did not complete, will set all INSTALLING versions for host {} to INSTALL_FAILED.", event.getHostname()); } else { // Parse structured output try { newHostState = RepositoryVersionState.INSTALLED; DistributeRepositoriesStructuredOutput structuredOutput = StageUtils.getGson().fromJson( event.getCommandReport().getStructuredOut(), DistributeRepositoriesStructuredOutput.class); repositoryVersion = structuredOutput.getInstalledRepositoryVersion(); // Handle the case in which the version to install did not contain the build number, // but the structured output does contain the build number. if (null != structuredOutput.getActualVersion() && !structuredOutput.getActualVersion().isEmpty() && null != structuredOutput.getInstalledRepositoryVersion() && !structuredOutput.getInstalledRepositoryVersion().isEmpty() && null != structuredOutput.getStackId() && !structuredOutput.getStackId().isEmpty() && !structuredOutput.getActualVersion().equals(structuredOutput.getInstalledRepositoryVersion())) { // !!! getInstalledRepositoryVersion() from the agent is the one // entered in the UI. getActualVersion() is computed. StackId stackId = new StackId(structuredOutput.getStackId()); RepositoryVersionEntity version = repoVersionDAO.findByStackAndVersion( stackId, structuredOutput.getInstalledRepositoryVersion()); if (null != version) { LOG.info("Repository version {} was found, but {} is the actual value", structuredOutput.getInstalledRepositoryVersion(), structuredOutput.getActualVersion()); // !!! the entered version is not correct version.setVersion(structuredOutput.getActualVersion()); repoVersionDAO.merge(version); repositoryVersion = structuredOutput.getActualVersion(); } else { // !!! extra check that the actual version is correct stackId = new StackId(structuredOutput.getStackId()); version = repoVersionDAO.findByStackAndVersion(stackId, structuredOutput.getActualVersion()); LOG.debug("Repository version {} was not found, check for {}. Found={}", structuredOutput.getInstalledRepositoryVersion(), structuredOutput.getActualVersion(), Boolean.valueOf(null != version)); if (null != version) { repositoryVersion = structuredOutput.getActualVersion(); } } } } catch (JsonSyntaxException e) { LOG.error("Cannot parse structured output %s", e); } } List<HostVersionEntity> hostVersions = hostVersionDAO.get().findByHost(event.getHostname()); // We have to iterate over all host versions for this host. Otherwise server-side command aborts (that do not // provide exact host stack version info) would be ignored for (HostVersionEntity hostVersion : hostVersions) { if (! event.isEmulated() && // Emulated events anyway can not provide actual repo version ! (repositoryVersion == null || hostVersion.getRepositoryVersion().getVersion().equals(repositoryVersion))) { continue; } // If repository version is null, it means that we were not able to determine any information (perhaps structured-out was empty), // so we should transition from INSTALLING to INSTALL_FAILED // If we know exact host stack version, there will be single execution of a code below if (hostVersion.getState() == RepositoryVersionState.INSTALLING) { hostVersion.setState(newHostState); hostVersionDAO.get().merge(hostVersion); // Update state of a cluster stack version try { Cluster cluster = clusters.get().getClusterById(clusterId); cluster.recalculateClusterVersionState(hostVersion.getRepositoryVersion()); } catch (AmbariException e) { LOG.error("Cannot get cluster with Id " + clusterId.toString() + " to recalculate its ClusterVersion.", e); } } } } }