/** * 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.actionmanager; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import javax.annotation.Nullable; import org.apache.ambari.server.Role; import org.apache.ambari.server.RoleCommand; import org.apache.ambari.server.agent.AgentCommand.AgentCommandType; import org.apache.ambari.server.agent.ExecutionCommand; import org.apache.ambari.server.metadata.RoleCommandPair; import org.apache.ambari.server.orm.dao.HostRoleCommandDAO; import org.apache.ambari.server.orm.entities.HostRoleCommandEntity; import org.apache.ambari.server.orm.entities.RoleSuccessCriteriaEntity; import org.apache.ambari.server.orm.entities.StageEntity; import org.apache.ambari.server.serveraction.ServerAction; import org.apache.ambari.server.state.Cluster; import org.apache.ambari.server.state.Clusters; import org.apache.ambari.server.state.Host; import org.apache.ambari.server.state.ServiceComponentHostEvent; import org.apache.ambari.server.state.svccomphost.ServiceComponentHostServerActionEvent; import org.apache.ambari.server.utils.StageUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.Assert; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; import com.google.inject.persist.Transactional; //This class encapsulates the stage. The stage encapsulates all the information //required to persist an action. public class Stage { /** * Used because in-memory storage of commands requires a hostname for maps * when the underlying store does not (host_id is {@code null}). We also * don't want stages getting confused with Ambari vs cluster hosts, so * don't use {@link StageUtils#getHostName()} */ public static final String INTERNAL_HOSTNAME = "_internal_ambari"; private static Logger LOG = LoggerFactory.getLogger(Stage.class); private final long requestId; private String clusterName; private long clusterId = -1L; private long stageId = -1; private final String logDir; private final String requestContext; private HostRoleStatus status = HostRoleStatus.PENDING; private HostRoleStatus displayStatus = HostRoleStatus.PENDING; private String commandParamsStage; private String hostParamsStage; private CommandExecutionType commandExecutionType = CommandExecutionType.STAGE; private boolean skippable; private boolean supportsAutoSkipOnFailure; private int stageTimeout = -1; private volatile boolean wrappersLoaded = false; //Map of roles to successFactors for this stage. Default is 1 i.e. 100% private Map<Role, Float> successFactors = new HashMap<>(); //Map of host to host-roles Map<String, Map<String, HostRoleCommand>> hostRoleCommands = new TreeMap<>(); private Map<String, List<ExecutionCommandWrapper>> commandsToSend = new TreeMap<>(); @Inject private HostRoleCommandFactory hostRoleCommandFactory; @Inject private ExecutionCommandWrapperFactory ecwFactory; @AssistedInject public Stage(@Assisted long requestId, @Assisted("logDir") String logDir, @Assisted("clusterName") @Nullable String clusterName, @Assisted("clusterId") long clusterId, @Assisted("requestContext") @Nullable String requestContext, @Assisted("commandParamsStage") String commandParamsStage, @Assisted("hostParamsStage") String hostParamsStage, HostRoleCommandFactory hostRoleCommandFactory, ExecutionCommandWrapperFactory ecwFactory) { wrappersLoaded = true; this.requestId = requestId; this.logDir = logDir; this.clusterName = clusterName; this.clusterId = clusterId; this.requestContext = requestContext == null ? "" : requestContext; this.commandParamsStage = commandParamsStage; this.hostParamsStage = hostParamsStage; skippable = false; supportsAutoSkipOnFailure = false; this.hostRoleCommandFactory = hostRoleCommandFactory; this.ecwFactory = ecwFactory; } @AssistedInject public Stage(@Assisted StageEntity stageEntity, HostRoleCommandDAO hostRoleCommandDAO, ActionDBAccessor dbAccessor, Clusters clusters, HostRoleCommandFactory hostRoleCommandFactory, ExecutionCommandWrapperFactory ecwFactory) { this.hostRoleCommandFactory = hostRoleCommandFactory; this.ecwFactory = ecwFactory; requestId = stageEntity.getRequestId(); stageId = stageEntity.getStageId(); skippable = stageEntity.isSkippable(); supportsAutoSkipOnFailure = stageEntity.isAutoSkipOnFailureSupported(); logDir = stageEntity.getLogInfo(); clusterId = stageEntity.getClusterId().longValue(); if (-1L != clusterId) { try { clusterName = clusters.getClusterById(clusterId).getClusterName(); } catch (Exception e) { LOG.debug("Could not load cluster with id {}, the cluster may have been removed for stage {}", Long.valueOf(clusterId), Long.valueOf(stageId)); } } requestContext = stageEntity.getRequestContext(); commandParamsStage = stageEntity.getCommandParamsStage(); hostParamsStage = stageEntity.getHostParamsStage(); commandExecutionType = stageEntity.getCommandExecutionType(); status = stageEntity.getStatus(); displayStatus = stageEntity.getDisplayStatus(); List<Long> taskIds = hostRoleCommandDAO.findTaskIdsByStage(requestId, stageId); Collection<HostRoleCommand> commands = dbAccessor.getTasks(taskIds); for (HostRoleCommand command : commands) { // !!! some commands won't have a hostname, because they are server-side and // don't hold that information. In that case, use the special key to // use in the map String hostname = getSafeHost(command.getHostName()); if (!hostRoleCommands.containsKey(hostname)) { hostRoleCommands.put(hostname, new LinkedHashMap<String, HostRoleCommand>()); } hostRoleCommands.get(hostname).put(command.getRole().toString(), command); } for (RoleSuccessCriteriaEntity successCriteriaEntity : stageEntity.getRoleSuccessCriterias()) { successFactors.put(successCriteriaEntity.getRole(), successCriteriaEntity.getSuccessFactor().floatValue()); } } /** * Creates object to be persisted in database * @return StageEntity */ public synchronized StageEntity constructNewPersistenceEntity() { StageEntity stageEntity = new StageEntity(); stageEntity.setRequestId(requestId); stageEntity.setStageId(getStageId()); stageEntity.setLogInfo(logDir); stageEntity.setSkippable(skippable); stageEntity.setAutoSkipFailureSupported(supportsAutoSkipOnFailure); stageEntity.setRequestContext(requestContext); stageEntity.setHostRoleCommands(new ArrayList<HostRoleCommandEntity>()); stageEntity.setRoleSuccessCriterias(new ArrayList<RoleSuccessCriteriaEntity>()); stageEntity.setCommandParamsStage(commandParamsStage); stageEntity.setHostParamsStage(hostParamsStage); stageEntity.setCommandExecutionType(commandExecutionType); stageEntity.setStatus(status); stageEntity.setDisplayStatus(displayStatus); for (Role role : successFactors.keySet()) { RoleSuccessCriteriaEntity roleSuccessCriteriaEntity = new RoleSuccessCriteriaEntity(); roleSuccessCriteriaEntity.setRole(role); roleSuccessCriteriaEntity.setStage(stageEntity); roleSuccessCriteriaEntity.setSuccessFactor(successFactors.get(role).doubleValue()); stageEntity.getRoleSuccessCriterias().add(roleSuccessCriteriaEntity); } return stageEntity; } void checkWrappersLoaded() { if (!wrappersLoaded) { synchronized (this) { // Stages are not used concurrently now, but it won't be performance loss if (!wrappersLoaded) { loadExecutionCommandWrappers(); } } } } @Transactional void loadExecutionCommandWrappers() { for (Map.Entry<String, Map<String, HostRoleCommand>> hostRoleCommandEntry : hostRoleCommands.entrySet()) { String hostname = hostRoleCommandEntry.getKey(); commandsToSend.put(hostname, new ArrayList<ExecutionCommandWrapper>()); Map<String, HostRoleCommand> roleCommandMap = hostRoleCommandEntry.getValue(); for (Map.Entry<String, HostRoleCommand> roleCommandEntry : roleCommandMap.entrySet()) { commandsToSend.get(hostname).add(roleCommandEntry.getValue().getExecutionCommandWrapper()); } } } public List<HostRoleCommand> getOrderedHostRoleCommands() { List<HostRoleCommand> commands = new ArrayList<>(); //Correct due to ordered maps for (Map.Entry<String, Map<String, HostRoleCommand>> hostRoleCommandEntry : hostRoleCommands.entrySet()) { for (Map.Entry<String, HostRoleCommand> roleCommandEntry : hostRoleCommandEntry.getValue().entrySet()) { commands.add(roleCommandEntry.getValue()); } } return commands; } /** * Returns <Role, RoleCommand> pairs which are in progress. * @return */ public Set<RoleCommandPair> getHostRolesInProgress() { Set<RoleCommandPair> commandsToScheduleSet = new HashSet<>(); for (Map.Entry<String, Map<String, HostRoleCommand>> hostRoleCommandEntry : hostRoleCommands.entrySet()) { for (Map.Entry<String, HostRoleCommand> roleCommandEntry : hostRoleCommandEntry.getValue().entrySet()) { if (HostRoleStatus.IN_PROGRESS_STATUSES.contains(roleCommandEntry.getValue().getStatus())) { commandsToScheduleSet.add( new RoleCommandPair(roleCommandEntry.getValue().getRole(), roleCommandEntry.getValue().getRoleCommand())); } } } return commandsToScheduleSet; } public String getCommandParamsStage() { return commandParamsStage; } public void setCommandParamsStage(String commandParamsStage) { this.commandParamsStage = commandParamsStage; } public String getHostParamsStage() { return hostParamsStage; } public void setHostParamsStage(String hostParamsStage) { this.hostParamsStage = hostParamsStage; } public CommandExecutionType getCommandExecutionType() { return commandExecutionType; } public void setCommandExecutionType(CommandExecutionType commandExecutionType) { this.commandExecutionType = commandExecutionType; } /** * get current status of the stage * @return {@link HostRoleStatus} */ public HostRoleStatus getStatus() { return status; } /** * sets status of the stage * @param status {@link HostRoleStatus} */ public void setStatus(HostRoleStatus status) { this.status = status; } public synchronized void setStageId(long stageId) { if (this.stageId != -1) { throw new RuntimeException("Attempt to set stageId again! Not allowed."); } //used on stage creation only, no need to check if wrappers loaded this.stageId = stageId; for (String host: commandsToSend.keySet()) { for (ExecutionCommandWrapper wrapper : commandsToSend.get(host)) { ExecutionCommand cmd = wrapper.getExecutionCommand(); cmd.setRequestAndStage(requestId, stageId); } } } public synchronized long getStageId() { return stageId; } public String getActionId() { return StageUtils.getActionId(requestId, getStageId()); } private synchronized ExecutionCommandWrapper addGenericExecutionCommand(String clusterName, String hostName, Role role, RoleCommand command, ServiceComponentHostEvent event, boolean retryAllowed, boolean autoSkipFailure) { boolean isHostRoleCommandAutoSkippable = autoSkipFailure && supportsAutoSkipOnFailure && skippable; // used on stage creation only, no need to check if wrappers loaded HostRoleCommand hrc = hostRoleCommandFactory.create(hostName, role, event, command, retryAllowed, isHostRoleCommandAutoSkippable); return addGenericExecutionCommand(clusterName, hostName, role, command, event, hrc); } private ExecutionCommandWrapper addGenericExecutionCommand(Cluster cluster, Host host, Role role, RoleCommand command, ServiceComponentHostEvent event, boolean retryAllowed, boolean autoSkipFailure) { boolean isHostRoleCommandAutoSkippable = autoSkipFailure && supportsAutoSkipOnFailure && skippable; HostRoleCommand hrc = hostRoleCommandFactory.create(host, role, event, command, retryAllowed, isHostRoleCommandAutoSkippable); return addGenericExecutionCommand(cluster.getClusterName(), host.getHostName(), role, command, event, hrc); } //TODO refactor method to use Host object (host_id support) private ExecutionCommandWrapper addGenericExecutionCommand(String clusterName, String hostName, Role role, RoleCommand command, ServiceComponentHostEvent event, HostRoleCommand hrc) { ExecutionCommand cmd = new ExecutionCommand(); ExecutionCommandWrapper wrapper = ecwFactory.createFromCommand(cmd); hrc.setExecutionCommandWrapper(wrapper); cmd.setHostname(hostName); cmd.setClusterName(clusterName); cmd.setRequestAndStage(requestId, stageId); cmd.setRole(role.name()); cmd.setRoleCommand(command); cmd.setServiceName(""); Map<String, HostRoleCommand> hrcMap = hostRoleCommands.get(hostName); if (hrcMap == null) { hrcMap = new LinkedHashMap<>(); hostRoleCommands.put(hostName, hrcMap); } if (hrcMap.get(role.toString()) != null) { throw new RuntimeException( "Setting the host role command second time for same stage: stage=" + getActionId() + ", host=" + hostName + ", role=" + role); } hrcMap.put(role.toString(), hrc); List<ExecutionCommandWrapper> execCmdList = commandsToSend.get(hostName); if (execCmdList == null) { execCmdList = new ArrayList<>(); commandsToSend.put(hostName, execCmdList); } if (execCmdList.contains(wrapper)) { //todo: proper exception throw new RuntimeException( "Setting the execution command second time for same stage: stage=" + getActionId() + ", host=" + hostName + ", role=" + role+ ", event="+event); } execCmdList.add(wrapper); return wrapper; } /** * A new host role command is created for execution. Creates both * ExecutionCommand and HostRoleCommand objects and adds them to the Stage. * This should be called only once for a host-role for a given stage. */ public synchronized void addHostRoleExecutionCommand(String host, Role role, RoleCommand command, ServiceComponentHostEvent event, String clusterName, String serviceName, boolean retryAllowed, boolean autoSkipFailure) { boolean isHostRoleCommandAutoSkippable = autoSkipFailure && supportsAutoSkipOnFailure && skippable; ExecutionCommandWrapper commandWrapper = addGenericExecutionCommand(clusterName, host, role, command, event, retryAllowed, isHostRoleCommandAutoSkippable); commandWrapper.getExecutionCommand().setServiceName(serviceName); } /** * A new host role command is created for execution. Creates both * ExecutionCommand and HostRoleCommand objects and adds them to the Stage. * This should be called only once for a host-role for a given stage. */ public synchronized void addHostRoleExecutionCommand(Host host, Role role, RoleCommand command, ServiceComponentHostEvent event, Cluster cluster, String serviceName, boolean retryAllowed, boolean autoSkipFailure) { boolean isHostRoleCommandAutoSkippable = autoSkipFailure && supportsAutoSkipOnFailure && skippable; ExecutionCommandWrapper commandWrapper = addGenericExecutionCommand(cluster, host, role, command, event, retryAllowed, isHostRoleCommandAutoSkippable); commandWrapper.getExecutionCommand().setServiceName(serviceName); } /** * <p/> * Creates server-side execution command. * <p/> * The action name for this command is expected to be the classname of a * {@link org.apache.ambari.server.serveraction.ServerAction} implementation * which will be instantiated and invoked as needed. * * @param actionName * a String declaring the action name (in the form of a classname) to * execute * @param userName * the name of the user who created this stage; may be null for * anonymous user * @param role * the Role for this command * @param command * the RoleCommand for this command * @param clusterName * a String identifying the cluster on which to to execute this * command * @param event * a ServiceComponentHostServerActionEvent * @param commandParams * a Map of String to String data used to pass to the action - this * may be empty or null if no data is relevant * @param commandDetail * a String declaring a descriptive name to pass to the action - null * or an empty string indicates no value is to be set * @param configTags * a Map of configuration tags to set for this command - if null, no * configurations will be available for the command * @param timeout * an Integer declaring the timeout for this action - if null, a * default * @param retryAllowed * indicates whether retry after failure is allowed */ public synchronized void addServerActionCommand(String actionName, @Nullable String userName, Role role, RoleCommand command, String clusterName, ServiceComponentHostServerActionEvent event, @Nullable Map<String, String> commandParams, @Nullable String commandDetail, @Nullable Map<String, Map<String, String>> configTags, @Nullable Integer timeout, boolean retryAllowed, boolean autoSkipFailure) { boolean isHostRoleCommandAutoSkippable = autoSkipFailure && supportsAutoSkipOnFailure && skippable; ExecutionCommandWrapper commandWrapper = addGenericExecutionCommand(clusterName, INTERNAL_HOSTNAME, role, command, event, retryAllowed, isHostRoleCommandAutoSkippable); ExecutionCommand cmd = commandWrapper.getExecutionCommand(); Map<String, String> cmdParams = new HashMap<>(); if (commandParams != null) { cmdParams.putAll(commandParams); } if (timeout != null) { cmdParams.put(ExecutionCommand.KeyNames.COMMAND_TIMEOUT, Long.toString(timeout)); } cmd.setCommandParams(cmdParams); Map<String, Map<String, String>> configurations = new TreeMap<>(); cmd.setConfigurations(configurations); Map<String, Map<String, Map<String, String>>> configurationAttributes = new TreeMap<>(); cmd.setConfigurationAttributes(configurationAttributes); if (configTags == null) { configTags = new TreeMap<>(); } cmd.setConfigurationTags(configTags); Map<String, String> roleParams = new HashMap<>(); roleParams.put(ServerAction.ACTION_NAME, actionName); if (userName != null) { roleParams.put(ServerAction.ACTION_USER_NAME, userName); } cmd.setRoleParams(roleParams); if(commandDetail != null) { HostRoleCommand hostRoleCommand = getHostRoleCommand(INTERNAL_HOSTNAME, role.toString()); if (hostRoleCommand != null) { hostRoleCommand.setCommandDetail(commandDetail); hostRoleCommand.setCustomCommandName(actionName); } } } /** * Adds cancel command to stage for given cancelTargets collection of * task id's that has to be canceled in Agent layer. */ public synchronized void addCancelRequestCommand(List<Long> cancelTargets, String clusterName, String hostName) { ExecutionCommandWrapper commandWrapper = addGenericExecutionCommand(clusterName, hostName, Role.AMBARI_SERVER_ACTION, RoleCommand.ABORT, null, false, false); ExecutionCommand cmd = commandWrapper.getExecutionCommand(); cmd.setCommandType(AgentCommandType.CANCEL_COMMAND); Assert.notEmpty(cancelTargets, "Provided targets task Id are empty."); Map<String, String> roleParams = new HashMap<>(); roleParams.put("cancelTaskIdTargets", StringUtils.join(cancelTargets, ',')); cmd.setRoleParams(roleParams); } /** * * @return list of hosts */ public synchronized List<String> getHosts() { // TODO: Check whether method should be synchronized List<String> hlist = new ArrayList<>(); for (String h : hostRoleCommands.keySet()) { hlist.add(h); } return hlist; } synchronized float getSuccessFactor(Role r) { Float f = successFactors.get(r); if (f == null) { if (r.equals(Role.DATANODE) || r.equals(Role.TASKTRACKER) || r.equals(Role.GANGLIA_MONITOR) || r.equals(Role.HBASE_REGIONSERVER)) { return (float) 0.5; } else { return 1; } } else { return f; } } public synchronized void setSuccessFactors(Map<Role, Float> suc) { successFactors = suc; } public synchronized Map<Role, Float> getSuccessFactors() { return successFactors; } public long getRequestId() { return requestId; } public String getClusterName() { return clusterName; } public long getClusterId() { return clusterId; } public String getRequestContext() { return requestContext; } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @return the last attempt time */ public long getLastAttemptTime(String hostname, String role) { return hostRoleCommands.get(getSafeHost(hostname)).get(role).getLastAttemptTime(); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @return the number of attempts */ public short getAttemptCount(String hostname, String role) { return hostRoleCommands.get(getSafeHost(hostname)).get(role).getAttemptCount(); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role */ public void incrementAttemptCount(String hostname, String role) { hostRoleCommands.get(getSafeHost(hostname)).get(role).incrementAttemptCount(); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @param t the last time the role was attempted */ public void setLastAttemptTime(String hostname, String role, long t) { hostRoleCommands.get(getSafeHost(hostname)).get(role).setLastAttemptTime(t); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @return the wrapper */ public ExecutionCommandWrapper getExecutionCommandWrapper(String hostname, String role) { HostRoleCommand hrc = hostRoleCommands.get(getSafeHost(hostname)).get(role); if (hrc != null) { return hrc.getExecutionCommandWrapper(); } else { return null; } } /** * @param hostname the hostname; {@code null} for a server-side stage * @return the list of commands for the host */ public List<ExecutionCommandWrapper> getExecutionCommands(String hostname) { checkWrappersLoaded(); return commandsToSend.get(getSafeHost(hostname)); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @return the start time for the task */ public long getStartTime(String hostname, String role) { return hostRoleCommands.get(getSafeHost(hostname)).get(role).getStartTime(); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @param startTime the start time */ public void setStartTime(String hostname, String role, long startTime) { hostRoleCommands.get(getSafeHost(hostname)).get(role).setStartTime(startTime); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @return the status */ public HostRoleStatus getHostRoleStatus(String hostname, String role) { return hostRoleCommands.get(getSafeHost(hostname)).get(role).getStatus(); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @param status the status */ public void setHostRoleStatus(String hostname, String role, HostRoleStatus status) { hostRoleCommands.get(getSafeHost(hostname)).get(role).setStatus(status); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param roleStr the role name * @return the wrapper event */ public ServiceComponentHostEventWrapper getFsmEvent(String hostname, String roleStr) { return hostRoleCommands.get(getSafeHost(hostname)).get(roleStr).getEvent(); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @param exitCode the exit code */ public void setExitCode(String hostname, String role, int exitCode) { hostRoleCommands.get(getSafeHost(hostname)).get(role).setExitCode(exitCode); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @return the exit code */ public int getExitCode(String hostname, String role) { return hostRoleCommands.get(getSafeHost(hostname)).get(role).getExitCode(); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @param stdErr the standard error string */ public void setStderr(String hostname, String role, String stdErr) { hostRoleCommands.get(getSafeHost(hostname)).get(role).setStderr(stdErr); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @param stdOut the standard output string */ public void setStdout(String hostname, String role, String stdOut) { hostRoleCommands.get(getSafeHost(hostname)).get(role).setStdout(stdOut); } public synchronized boolean isStageInProgress() { for(String host: hostRoleCommands.keySet()) { for (String role : hostRoleCommands.get(host).keySet()) { HostRoleCommand hrc = hostRoleCommands.get(host).get(role); if (hrc == null) { return false; } if (hrc.getStatus().equals(HostRoleStatus.PENDING) || hrc.getStatus().equals(HostRoleStatus.QUEUED) || hrc.getStatus().equals(HostRoleStatus.IN_PROGRESS)) { return true; } } } return false; } public synchronized boolean doesStageHaveHostRoleStatus( Set<HostRoleStatus> statuses) { for(String host: hostRoleCommands.keySet()) { for (String role : hostRoleCommands.get(host).keySet()) { HostRoleCommand hrc = hostRoleCommands.get(host).get(role); if (hrc == null) { return false; } for (HostRoleStatus status : statuses) { if (hrc.getStatus().equals(status)) { return true; } } } } return false; } public Map<String, List<ExecutionCommandWrapper>> getExecutionCommands() { checkWrappersLoaded(); return commandsToSend; } public String getLogDir() { return logDir; } public Map<String, Map<String, HostRoleCommand>> getHostRoleCommands() { return hostRoleCommands; } /** * Gets the {@link HostRoleCommand} matching the specified ID from this stage. * This will not hit the database, instead using the pre-cached list of HRCs * from the construction of the stage. * * @param taskId * the ID to match * @return the {@link HostRoleCommand} or {@code null} if none match. */ public HostRoleCommand getHostRoleCommand(long taskId) { for (Map.Entry<String, Map<String, HostRoleCommand>> hostEntry : hostRoleCommands.entrySet()) { Map<String, HostRoleCommand> hostCommands = hostEntry.getValue(); for (Map.Entry<String, HostRoleCommand> hostCommand : hostCommands.entrySet()) { HostRoleCommand hostRoleCommand = hostCommand.getValue(); if (null != hostRoleCommand && hostRoleCommand.getTaskId() == taskId) { return hostRoleCommand; } } } return null; } /** * This method should be used only in stage planner. To add * a new execution command use * {@link #addHostRoleExecutionCommand(String, Role, RoleCommand, ServiceComponentHostEvent, String, String, boolean, boolean)} * @param origStage the stage * @param hostname the hostname; {@code null} for a server-side stage * @param r the role */ public synchronized void addExecutionCommandWrapper(Stage origStage, String hostname, Role r) { //used on stage creation only, no need to check if wrappers loaded hostname = getSafeHost(hostname); String role = r.toString(); if (commandsToSend.get(hostname) == null) { commandsToSend.put(hostname, new ArrayList<ExecutionCommandWrapper>()); } commandsToSend.get(hostname).add( origStage.getExecutionCommandWrapper(hostname, role)); if (hostRoleCommands.get(hostname) == null) { hostRoleCommands.put(hostname, new LinkedHashMap<String, HostRoleCommand>()); } // TODO add reference to ExecutionCommand into HostRoleCommand hostRoleCommands.get(hostname).put(role, origStage.getHostRoleCommand(hostname, role)); } /** * @param hostname the hostname; {@code null} for a server-side stage * @param role the role * @return the role command */ public HostRoleCommand getHostRoleCommand(String hostname, String role) { return hostRoleCommands.get(getSafeHost(hostname)).get(role); } /** * In this method we sum up all timeout values for all commands inside stage */ public synchronized int getStageTimeout() { checkWrappersLoaded(); if (stageTimeout == -1) { for (String host: commandsToSend.keySet()) { int summaryTaskTimeoutForHost = 0; for (ExecutionCommandWrapper command : commandsToSend.get(host)) { Map<String, String> commandParams = command.getExecutionCommand().getCommandParams(); String timeoutKey = ExecutionCommand.KeyNames.COMMAND_TIMEOUT; if (commandParams != null && commandParams.containsKey(timeoutKey)) { String timeoutStr = commandParams.get(timeoutKey); long commandTimeout = Long.parseLong(timeoutStr) * 1000; // Converting to milliseconds summaryTaskTimeoutForHost += commandTimeout; } else { LOG.error("Execution command has no timeout parameter" + command.toString()); } } if (summaryTaskTimeoutForHost > stageTimeout) { stageTimeout = summaryTaskTimeoutForHost; } } } return stageTimeout; } /** * Determine whether or not this stage is skippable. * * A skippable stage can be skipped on failure so that the * remaining stages of the request can execute. * If a stage is not skippable, a failure will cause the * remaining stages of the request to be aborted. * * @return true if this stage is skippable */ public boolean isSkippable() { return skippable; } /** * Set skippable for this stage. * * A skippable stage can be skipped on failure so that the * remaining stages of the request can execute. * If a stage is not skippable, a failure will cause the * remaining stages of the request to be aborted. * * @param skippable true if this stage should be skippable */ public void setSkippable(boolean skippable) { this.skippable = skippable; } /** * Determine whether this stage supports automatically skipping failures of * its commands. * * @return {@code true} if this stage supports automatically skipping failures * of its commands. */ public boolean isAutoSkipOnFailureSupported() { return supportsAutoSkipOnFailure; } /** * Sets whether this stage supports automatically skipping failures of its * commands. * * @param supportsAutoSkipOnFailure * {@code true} if this stage supports automatically skipping * failures of its commands. */ public void setAutoSkipFailureSupported(boolean supportsAutoSkipOnFailure) { this.supportsAutoSkipOnFailure = supportsAutoSkipOnFailure; } @Override //Object public synchronized String toString() { StringBuilder builder = new StringBuilder(); builder.append("STAGE DESCRIPTION BEGIN\n"); builder.append("requestId=").append(requestId).append("\n"); builder.append("stageId=").append(stageId).append("\n"); builder.append("clusterName=").append(clusterName).append("\n"); builder.append("logDir=").append(logDir).append("\n"); builder.append("requestContext=").append(requestContext).append("\n"); builder.append("commandParamsStage=").append(commandParamsStage).append("\n"); builder.append("hostParamsStage=").append(hostParamsStage).append("\n"); builder.append("status=").append(status).append("\n"); builder.append("displayStatus=").append(displayStatus).append("\n"); builder.append("Success Factors:\n"); for (Role r : successFactors.keySet()) { builder.append(" role: ").append(r).append(", factor: ").append(successFactors.get(r)).append("\n"); } for (HostRoleCommand hostRoleCommand : getOrderedHostRoleCommands()) { builder.append("HOST: ").append(hostRoleCommand.getHostName()).append(" :\n"); builder.append(hostRoleCommand.getExecutionCommandWrapper().getJson()); builder.append("\n"); builder.append(hostRoleCommand.toString()); builder.append("\n"); } builder.append("STAGE DESCRIPTION END\n"); return builder.toString(); } /** * Helper to make sure the hostname is non-null for internal command map. * @param hostname the hostname for the map key * @return the hostname when not {@code null}, otherwise {@link #INTERNAL_HOSTNAME} */ private static String getSafeHost(String hostname) { return (null == hostname) ? INTERNAL_HOSTNAME : hostname; } }