blob: c3d586cb5dc348e9c01613f66a960f70d31147ab [file] [log] [blame]
/*
* 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;
}
}