| /* |
| * 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.stack.upgrade.orchestrate; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.ambari.server.serveraction.upgrades.AutoSkipFailedSummaryAction; |
| import org.apache.ambari.server.stack.HostsType; |
| import org.apache.ambari.server.stack.upgrade.Grouping; |
| import org.apache.ambari.server.stack.upgrade.ParallelScheduler; |
| import org.apache.ambari.server.stack.upgrade.ServerActionTask; |
| import org.apache.ambari.server.stack.upgrade.ServiceCheckGrouping; |
| import org.apache.ambari.server.stack.upgrade.Task; |
| import org.apache.ambari.server.stack.upgrade.UpgradePack.ProcessingComponent; |
| import org.apache.ambari.server.state.ConfigHelper; |
| import org.apache.commons.collections.CollectionUtils; |
| import org.apache.commons.lang.StringUtils; |
| |
| /** |
| * Defines how to build stages for an Upgrade or Downgrade. |
| */ |
| public abstract class StageWrapperBuilder { |
| |
| /** |
| * The message for the task which checks for skipped failures. |
| */ |
| private static final String AUTO_SKIPPED_TASK_SUMMARY = "Pauses the upgrade if there were failed steps that were automatically skipped."; |
| |
| /** |
| * The message to show when the upgrade is paused due to auto-skipped failures |
| */ |
| private static final String AUTO_SKIPPED_MESSAGE = "There are failures that were automatically skipped. Review the failures before continuing."; |
| |
| /** |
| * The upgrade/downgrade grouping that the builder is for. |
| */ |
| protected final Grouping m_grouping; |
| |
| /** |
| * Constructor. |
| * |
| * @param grouping |
| * the upgrade/downgrade grouping (not {@code null}). |
| */ |
| protected StageWrapperBuilder(Grouping grouping) { |
| m_grouping = grouping; |
| } |
| |
| /** |
| * Adds a processing component that will be built into stage wrappers. |
| * |
| * @param upgradeContext |
| * the upgrade context |
| * @param hostsType |
| * the hosts, along with their type |
| * @param service |
| * the service name |
| * @param clientOnly |
| * whether the service is client only, no service checks |
| * @param pc |
| * the ProcessingComponent derived from the upgrade pack |
| * @param params |
| * additional parameters |
| */ |
| public abstract void add(UpgradeContext upgradeContext, HostsType hostsType, String service, |
| boolean clientOnly, ProcessingComponent pc, Map<String, String> params); |
| |
| /** |
| * Builds the stage wrappers, including any pre- and post-procesing that needs |
| * to be performed. |
| * |
| * @param upgradeContext |
| * the upgrade context (not {@code null}). |
| * @return a list of stages, never {@code null} |
| */ |
| public final List<StageWrapper> build(UpgradeContext upgradeContext) { |
| List<StageWrapper> stageWrappers = beforeBuild(upgradeContext); |
| stageWrappers = build(upgradeContext, stageWrappers); |
| stageWrappers = afterBuild(upgradeContext, stageWrappers); |
| return stageWrappers; |
| } |
| |
| /** |
| * Performs any pre-processing that needs to be performed on the list of stage |
| * wrappers. |
| * |
| * @param upgradeContext |
| * the upgrade context (not {@code null}). |
| * @return the initial list of stage wrappers, or an empty list (never |
| * {@code null}). |
| */ |
| protected List<StageWrapper> beforeBuild(UpgradeContext upgradeContext) { |
| List<StageWrapper> stageWrappers = new ArrayList<>(100); |
| return stageWrappers; |
| } |
| |
| /** |
| * Builds the stage wrappers. |
| * |
| * @param upgradeContext |
| * the upgrade context (not {@code null}). |
| * @param stageWrappers |
| * the list of stage wrappers created by |
| * {@link #beforeBuild(UpgradeContext)}. |
| * @return the stage wrapper list, (never {@code null}) |
| */ |
| public abstract List<StageWrapper> build(UpgradeContext upgradeContext, |
| List<StageWrapper> stageWrappers); |
| |
| /** |
| * Performs any post-processing that needs to be performed on the list of |
| * stage wrappers. |
| * |
| * @param upgradeContext |
| * the upgrade context (not {@code null}). |
| * @param stageWrappers |
| * the list of stage wrappers created by |
| * {@link #build(UpgradeContext, List)}. |
| * @return the post-processed list of stage wrappers (never {@code null}) |
| */ |
| protected List<StageWrapper> afterBuild(UpgradeContext upgradeContext, |
| List<StageWrapper> stageWrappers) { |
| |
| if (CollectionUtils.isEmpty(stageWrappers)) { |
| return stageWrappers; |
| } |
| |
| // we only want to insert the auto skip summary if the group is skippable |
| // and the upgrade context says to auto skip failures |
| final boolean autoSkipFailures; |
| if (m_grouping instanceof ServiceCheckGrouping) { |
| autoSkipFailures = upgradeContext.isServiceCheckFailureAutoSkipped(); |
| } else { |
| autoSkipFailures = upgradeContext.isComponentFailureAutoSkipped(); |
| } |
| |
| if (m_grouping.supportsAutoSkipOnFailure && m_grouping.skippable && autoSkipFailures) { |
| ServerActionTask skippedFailedCheck = new ServerActionTask(); |
| skippedFailedCheck.implClass = AutoSkipFailedSummaryAction.class.getName(); |
| skippedFailedCheck.summary = AUTO_SKIPPED_TASK_SUMMARY; |
| skippedFailedCheck.messages.add(AUTO_SKIPPED_MESSAGE); |
| |
| TaskWrapper skippedFailedTaskWrapper = new TaskWrapper(null, null, |
| Collections.emptySet(), skippedFailedCheck); |
| |
| StageWrapper skippedFailedStageWrapper = new StageWrapper( |
| StageWrapper.Type.SERVER_SIDE_ACTION, "Verifying Skipped Failures", |
| skippedFailedTaskWrapper); |
| |
| stageWrappers.add(skippedFailedStageWrapper); |
| } |
| |
| return stageWrappers; |
| } |
| |
| /** |
| * Consistently formats a string. |
| * @param prefix |
| * @param component |
| * @param batchNum |
| * @param totalBatches |
| * @return the prepared string |
| */ |
| protected String getStageText(String prefix, String component, Set<String> hosts, int batchNum, int totalBatches) { |
| String stageText = getStageText(prefix, component, hosts); |
| String batchText = 1 == totalBatches? "" : String.format(" (Batch %s of %s)", batchNum, totalBatches); |
| return stageText + batchText; |
| } |
| |
| /** |
| * Consistently formats a string. |
| * @param prefix |
| * @param component |
| * @param hosts |
| * @return the prepared string |
| */ |
| protected String getStageText(String prefix, String component, Set<String> hosts) { |
| return String.format("%s %s on %s%s", |
| prefix, |
| component, |
| 1 == hosts.size() ? hosts.iterator().next() : Integer.valueOf(hosts.size()), |
| 1 == hosts.size() ? "" : " hosts"); |
| } |
| |
| /** |
| * Determine the list of pre- or post-tasks given these rules |
| * <ul> |
| * <li>When performing an upgrade, only use upgrade tasks</li> |
| * <li>When performing a downgrade, use the downgrade tasks if they are defined</li> |
| * <li>When performing a downgrade, but no downgrade tasks exist, reuse the upgrade tasks</li> |
| * </ul> |
| * @param context the upgrade context |
| * @param preTasks {@code true} if loading pre-(upgrade|downgrade) or {@code false for post-(upgrade|downgrade) |
| * @param pc the processing component holding task definitions |
| * @return A collection, potentially empty, of the tasks to run, which may contain either |
| * pre or post tasks if they exist, and the order depends on whether it's an upgrade or downgrade. |
| */ |
| protected List<Task> resolveTasks(final UpgradeContext context, boolean preTasks, ProcessingComponent pc) { |
| if (null == pc) { |
| return Collections.emptyList(); |
| } |
| |
| boolean forUpgrade = context.getDirection().isUpgrade(); |
| |
| final List<Task> interim; |
| |
| if (forUpgrade) { |
| interim = preTasks ? pc.preTasks : pc.postTasks; |
| } else { |
| interim = preTasks ? pc.preDowngradeTasks : pc.postDowngradeTasks; |
| } |
| |
| if (CollectionUtils.isEmpty(interim)) { |
| return Collections.emptyList(); |
| } |
| |
| List<Task> tasks = new ArrayList<>(); |
| for (Task t : interim) { |
| boolean taskPassesScoping = context.isScoped(t.scope); |
| boolean taskPassesCondition = true; |
| |
| // tasks can have conditions on them, so check to make sure the condition is satisfied |
| if (null != t.condition && !t.condition.isSatisfied(context)) { |
| taskPassesCondition = false; |
| } |
| |
| if (taskPassesScoping && taskPassesCondition) { |
| tasks.add(t); |
| } |
| } |
| |
| return tasks; |
| } |
| |
| /** |
| * The upgrade packs are written such that there is one and only one upgrade element |
| * for a component, all other directives go in (pre|post)-(upgrade|downgrade) elements. |
| * @param pc the processing component |
| * @return the single task, or {@code null} if there is none |
| */ |
| protected Task resolveTask(UpgradeContext context, ProcessingComponent pc) { |
| if (null != pc.tasks && 1 == pc.tasks.size()) { |
| if (context.isScoped(pc.tasks.get(0).scope)) { |
| return pc.tasks.get(0); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Gets the parallel setting for a grouping, if defined. |
| * |
| * @param ctx |
| * the upgrade context |
| * @param defaultValue |
| * if the parallel scheduler is not found, return this value instead |
| * @return |
| * the count of hosts to run in parallel |
| */ |
| protected int getParallelHostCount(UpgradeContext ctx, int defaultValue) { |
| |
| if (m_grouping.parallelScheduler != null) { |
| int taskParallelism = m_grouping.parallelScheduler.maxDegreeOfParallelism; |
| String maxDegreeFromClusterEnv = |
| ctx.getResolver().getValueFromDesiredConfigurations(ConfigHelper.CLUSTER_ENV, "max_degree_parallelism"); |
| if (StringUtils.isNotEmpty(maxDegreeFromClusterEnv) && StringUtils.isNumeric(maxDegreeFromClusterEnv)) { |
| taskParallelism = Integer.parseInt(maxDegreeFromClusterEnv); |
| } |
| if (taskParallelism == ParallelScheduler.DEFAULT_MAX_DEGREE_OF_PARALLELISM) { |
| taskParallelism = ctx.getDefaultMaxDegreeOfParallelism(); |
| } |
| return taskParallelism; |
| } |
| |
| return defaultValue; |
| } |
| |
| } |