/* * 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.checks; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.ambari.annotations.Experimental; import org.apache.ambari.annotations.ExperimentalFeature; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.api.services.AmbariMetaInfo; import org.apache.ambari.server.configuration.Configuration; import org.apache.ambari.server.controller.PrereqCheckRequest; import org.apache.ambari.server.orm.dao.ClusterVersionDAO; import org.apache.ambari.server.orm.dao.HostVersionDAO; import org.apache.ambari.server.orm.dao.RepositoryVersionDAO; import org.apache.ambari.server.orm.dao.UpgradeDAO; 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.Config; import org.apache.ambari.server.state.DesiredConfig; import org.apache.ambari.server.state.RepositoryType; import org.apache.ambari.server.state.ServiceInfo; import org.apache.ambari.server.state.stack.PrereqCheckType; import org.apache.ambari.server.state.stack.PrerequisiteCheck; import org.apache.ambari.server.state.stack.UpgradePack; import org.apache.ambari.server.state.stack.upgrade.RepositoryVersionHelper; import org.apache.ambari.server.state.stack.upgrade.UpgradeType; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; import com.google.inject.Provider; /** * Describes prerequisite check. */ public abstract class AbstractCheckDescriptor { private static final Logger LOG = LoggerFactory.getLogger(AbstractCheckDescriptor.class); protected static final String DEFAULT = "default"; @Inject Provider<Clusters> clustersProvider; @Inject Provider<ClusterVersionDAO> clusterVersionDAOProvider; @Inject Provider<HostVersionDAO> hostVersionDaoProvider; @Inject Provider<RepositoryVersionDAO> repositoryVersionDaoProvider; @Inject Provider<UpgradeDAO> upgradeDaoProvider; @Inject Provider<RepositoryVersionHelper> repositoryVersionHelper; @Inject Provider<AmbariMetaInfo> ambariMetaInfo; @Inject Configuration config; private CheckDescription m_description; /** * Constructor. * * @param description description */ protected AbstractCheckDescriptor(CheckDescription description) { m_description = description; } /** * Tests if the prerequisite check is applicable to given cluster. This * method's default logic is to ensure that the cluster stack source and * target are compatible with the prerequisite check. When overridding this * method, call {@code super#isApplicable(PrereqCheckRequest)}. * * @param request * prerequisite check request * @return true if check should be performed * * @throws org.apache.ambari.server.AmbariException * if server error happens */ public boolean isApplicable(PrereqCheckRequest request) throws AmbariException { // this is default behaviour return true; } /** * Same like {@code isApplicable(PrereqCheckRequest request)}, but with service presence check * @param request * prerequisite check request * @param requiredServices * set of services, which need to be present to allow check execution * @param requiredAll * require all services in the list or at least one need to present * @return true if check should be performed * @throws org.apache.ambari.server.AmbariException * if server error happens */ @Experimental(feature = ExperimentalFeature.PATCH_UPGRADES) public boolean isApplicable(PrereqCheckRequest request, List<String> requiredServices, boolean requiredAll) throws AmbariException { final Cluster cluster = clustersProvider.get().getCluster(request.getClusterName()); Set<String> services = cluster.getServices().keySet(); // default return value depends on assign inside check block boolean serviceFound = requiredAll && !requiredServices.isEmpty(); for (String service : requiredServices) { if ( services.contains(service) && !requiredAll) { serviceFound = true; break; } else if (!services.contains(service) && requiredAll) { serviceFound = false; break; } } // !!! service is found and deployed - now check if it is part of the VDF if (serviceFound && null != request.getTargetStackId()) { String stackName = request.getTargetStackId().getStackName(); RepositoryVersionEntity rve = repositoryVersionDaoProvider.get(). findByStackNameAndVersion(stackName, request.getRepositoryVersion()); if (RepositoryType.STANDARD != rve.getType()) { try { Set<String> availableServices = rve.getRepositoryXml().getAvailableServiceNames(); if (!CollectionUtils.containsAny(availableServices, requiredServices)) { serviceFound = false; } } catch (Exception e) { LOG.warn("Could not parse xml for %s", request.getRepositoryVersion(), e); } } } return serviceFound; } /** * Executes check against given cluster. * * @param prerequisiteCheck dto for upgrade check results * @param request pre upgrade check request * * @throws AmbariException if server error happens */ public abstract void perform(PrerequisiteCheck prerequisiteCheck, PrereqCheckRequest request) throws AmbariException; /** * Gets the description of the check. * * @return the description (not {@code null}). */ public CheckDescription getDescription() { return m_description; } /** * Gets the type of check. * * @return the type of check (not {@code null}). */ public PrereqCheckType getType() { return m_description.getType(); } /** * Gets the default fail reason * @param prerequisiteCheck the check being performed * @param request the request * @return the failure string */ protected String getFailReason(PrerequisiteCheck prerequisiteCheck, PrereqCheckRequest request) { return getFailReason(DEFAULT, prerequisiteCheck, request); } /** * Gets a cluster configuration property if it exists, or {@code null} * otherwise. * * @param request * the request (not {@code null}). * @param configType * the configuration type, such as {@code hdfs-site} (not * {@code null}). * @param propertyName * the name of the property (not {@code null}). * @return the property value or {@code null} if not found. * @throws AmbariException */ protected String getProperty(PrereqCheckRequest request, String configType, String propertyName) throws AmbariException { final String clusterName = request.getClusterName(); final Cluster cluster = clustersProvider.get().getCluster(clusterName); final Map<String, DesiredConfig> desiredConfigs = cluster.getDesiredConfigs(); final DesiredConfig desiredConfig = desiredConfigs.get(configType); if (null == desiredConfig) { return null; } final Config config = cluster.getConfig(configType, desiredConfig.getTag()); Map<String, String> properties = config.getProperties(); return properties.get(propertyName); } /** * Gets the fail reason * @param key the failure text key * @param prerequisiteCheck the check being performed * @param request the request * @return the failure string */ protected String getFailReason(String key, PrerequisiteCheck prerequisiteCheck, PrereqCheckRequest request) { String fail = m_description.getFail(key); if (fail.contains("{{version}}") && null != request.getRepositoryVersion()) { fail = fail.replace("{{version}}", request.getRepositoryVersion()); } if (fail.contains("{{fails}}")) { LinkedHashSet<String> names = prerequisiteCheck.getFailedOn(); // If Type=PrereqCheckType.HOST, names list is already populated if (getDescription().getType() == PrereqCheckType.SERVICE) { Clusters clusters = clustersProvider.get(); AmbariMetaInfo metaInfo = ambariMetaInfo.get(); try { Cluster c = clusters.getCluster(request.getClusterName()); Map<String, ServiceInfo> services = metaInfo.getServices( c.getDesiredStackVersion().getStackName(), c.getDesiredStackVersion().getStackVersion()); LinkedHashSet<String> displays = new LinkedHashSet<>(); for (String name : names) { if (services.containsKey(name)) { displays.add(services.get(name).getDisplayName()); } else { displays.add(name); } } names = displays; } catch (Exception e) { LOG.warn("Could not load service info map"); } } fail = fail.replace("{{fails}}", formatEntityList(names)); } return fail; } /** * Formats lists of given entities to human readable form: * [entity1] -> {entity1} {noun} * [entity1, entity2] -> {entity1} and {entity2} {noun}s * [entity1, entity2, entity3] -> {entity1}, {entity2} and {entity3} {noun}s * The noun for the entities is taken from check type, it may be cluster, service or host. * * @param entities list of entities to format * @return formatted entity list */ protected String formatEntityList(LinkedHashSet<String> entities) { if (entities == null || entities.isEmpty()) { return ""; } final StringBuilder formatted = new StringBuilder(StringUtils.join(entities, ", ")); if (entities.size() > 1) { formatted.replace(formatted.lastIndexOf(","), formatted.lastIndexOf(",") + 1, " and"); } return formatted.toString(); } /** * Gets whether this upgrade check is required for the specified * {@link UpgradeType}. Checks which are marked as required do not need to be * explicitely declared in the {@link UpgradePack} to be run. * * @return {@code true} if it is required, {@code false} otherwise. */ public boolean isRequired(UpgradeType upgradeType) { UpgradeType[] upgradeTypes = getClass().getAnnotation(UpgradeCheck.class).required(); for (UpgradeType requiredType : upgradeTypes) { if (upgradeType == requiredType) { return true; } } return false; } /** * Return a boolean indicating whether or not configs allow bypassing errors during the RU/EU PreChecks. * @return */ public boolean isStackUpgradeAllowedToBypassPreChecks() { return config.isUpgradePrecheckBypass(); } }