/*
* 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.orm.entities;
import static org.apache.commons.lang.StringUtils.defaultString;
import java.util.Arrays;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import org.apache.ambari.server.Role;
import org.apache.ambari.server.RoleCommand;
import org.apache.ambari.server.actionmanager.HostRoleStatus;
import org.apache.commons.lang.ArrayUtils;
@Entity
@Table(name = "host_role_command"
, indexes = {
@Index(name = "idx_hrc_request_id", columnList = "request_id")
, @Index(name = "idx_hrc_status_role", columnList = "status, role")
})
@TableGenerator(name = "host_role_command_id_generator",
table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "sequence_value"
, pkColumnValue = "host_role_command_id_seq"
, initialValue = 1
)
@NamedQueries({
@NamedQuery(name = "HostRoleCommandEntity.findTaskIdsByRequestStageIds", query = "SELECT command.taskId FROM HostRoleCommandEntity command WHERE command.stageId = :stageId AND command.requestId = :requestId"),
@NamedQuery(name = "HostRoleCommandEntity.findCountByCommandStatuses", query = "SELECT COUNT(command.taskId) FROM HostRoleCommandEntity command WHERE command.status IN :statuses"),
@NamedQuery(name = "HostRoleCommandEntity.findByRequestIdAndStatuses", query="SELECT task FROM HostRoleCommandEntity task WHERE task.requestId=:requestId AND task.status IN :statuses ORDER BY task.taskId ASC"),
@NamedQuery(name = "HostRoleCommandEntity.findTasksByStatusesOrderByIdDesc", query = "SELECT task FROM HostRoleCommandEntity task WHERE task.requestId = :requestId AND task.status IN :statuses ORDER BY task.taskId DESC"),
@NamedQuery(name = "HostRoleCommandEntity.findNumTasksAlreadyRanInStage", query = "SELECT COUNT(task.taskId) FROM HostRoleCommandEntity task WHERE task.requestId = :requestId AND task.taskId > :taskId AND task.stageId > :stageId AND task.status NOT IN :statuses"),
@NamedQuery(name = "HostRoleCommandEntity.findByCommandStatuses", query = "SELECT command FROM HostRoleCommandEntity command WHERE command.status IN :statuses ORDER BY command.requestId, command.stageId"),
@NamedQuery(name = "HostRoleCommandEntity.findByHostId", query = "SELECT command FROM HostRoleCommandEntity command WHERE command.hostId=:hostId"),
@NamedQuery(name = "HostRoleCommandEntity.findByHostRole", query = "SELECT command FROM HostRoleCommandEntity command WHERE command.hostEntity.hostName=:hostName AND command.requestId=:requestId AND command.stageId=:stageId AND command.role=:role ORDER BY command.taskId"),
@NamedQuery(name = "HostRoleCommandEntity.findByHostRoleNullHost", query = "SELECT command FROM HostRoleCommandEntity command WHERE command.hostEntity IS NULL AND command.requestId=:requestId AND command.stageId=:stageId AND command.role=:role"),
@NamedQuery(name = "HostRoleCommandEntity.findByStatusBetweenStages", query = "SELECT command FROM HostRoleCommandEntity command WHERE command.requestId = :requestId AND command.stageId >= :minStageId AND command.stageId <= :maxStageId AND command.status = :status"),
@NamedQuery(name = "HostRoleCommandEntity.updateAutoSkipExcludeRoleCommand", query = "UPDATE HostRoleCommandEntity command SET command.autoSkipOnFailure = :autoSkipOnFailure WHERE command.requestId = :requestId AND command.roleCommand <> :roleCommand"),
@NamedQuery(name = "HostRoleCommandEntity.updateAutoSkipForRoleCommand", query = "UPDATE HostRoleCommandEntity command SET command.autoSkipOnFailure = :autoSkipOnFailure WHERE command.requestId = :requestId AND command.roleCommand = :roleCommand"),
@NamedQuery(name = "HostRoleCommandEntity.removeByTaskIds", query = "DELETE FROM HostRoleCommandEntity command WHERE command.taskId IN :taskIds"),
@NamedQuery(name = "HostRoleCommandEntity.findHostsByCommandStatus", query = "SELECT DISTINCT(host.hostName) FROM HostRoleCommandEntity command, HostEntity host WHERE (command.requestId >= :iLowestRequestIdInProgress AND command.requestId <= :iHighestRequestIdInProgress) AND command.status IN :statuses AND command.hostId = host.hostId AND host.hostName IS NOT NULL"),
@NamedQuery(name = "HostRoleCommandEntity.getBlockingHostsForRequest", query = "SELECT DISTINCT(host.hostName) FROM HostRoleCommandEntity command, HostEntity host WHERE command.requestId >= :lowerRequestIdInclusive AND command.requestId < :upperRequestIdExclusive AND command.status IN :statuses AND command.isBackgroundCommand=0 AND command.hostId = host.hostId AND host.hostName IS NOT NULL")
})
public class HostRoleCommandEntity {
private static int MAX_COMMAND_DETAIL_LENGTH = 250;
@Column(name = "task_id")
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "host_role_command_id_generator")
private Long taskId;
@Column(name = "request_id", insertable = false, updatable = false, nullable = false)
@Basic
private Long requestId;
@Column(name = "stage_id", insertable = false, updatable = false, nullable = false)
@Basic
private Long stageId;
@Column(name = "host_id", insertable = false, updatable = false, nullable = true)
@Basic
private Long hostId;
@Column(name = "role")
private String role;
@Column(name = "event", length = 32000)
@Basic
@Lob
private String event = "";
@Column(name = "exitcode", nullable = false)
@Basic
private Integer exitcode = 0;
@Column(name = "status", nullable = false)
@Enumerated(EnumType.STRING)
private HostRoleStatus status = HostRoleStatus.PENDING;
@Column(name = "std_error")
@Lob
@Basic
private byte[] stdError = new byte[0];
@Column(name = "std_out")
@Lob
@Basic
private byte[] stdOut = new byte[0];
@Column(name = "output_log")
@Basic
private String outputLog = null;
@Column(name = "error_log")
@Basic
private String errorLog = null;
@Column(name = "structured_out")
@Lob
@Basic
private byte[] structuredOut = new byte[0];
@Basic
@Column(name = "start_time", nullable = false)
private Long startTime = -1L;
/**
* Because the startTime is allowed to be overwritten, introduced a new column for the original start time.
*/
@Basic
@Column(name = "original_start_time", nullable = false)
private Long originalStartTime = -1L;
@Basic
@Column(name = "end_time", nullable = false)
private Long endTime = -1L;
@Basic
@Column(name = "last_attempt_time", nullable = false)
private Long lastAttemptTime = -1L;
@Basic
@Column(name = "attempt_count", nullable = false)
private Short attemptCount = 0;
@Column(name = "retry_allowed", nullable = false)
private Integer retryAllowed = Integer.valueOf(0);
/**
* If the command fails and is skippable, then this will instruct the
* scheduler to skip the command.
*/
@Column(name = "auto_skip_on_failure", nullable = false)
private Integer autoSkipOnFailure = Integer.valueOf(0);
// This is really command type as well as name
@Column(name = "role_command")
@Enumerated(EnumType.STRING)
private RoleCommand roleCommand;
// A readable description of the command
@Column(name = "command_detail")
@Basic
private String commandDetail;
// An optional property that can be used for setting the displayName for operations window
@Column(name = "ops_display_name")
@Basic
private String opsDisplayName;
// When command type id CUSTOM_COMMAND and CUSTOM_ACTION this is the name
@Column(name = "custom_command_name")
@Basic
private String customCommandName;
@OneToOne(mappedBy = "hostRoleCommand", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
private ExecutionCommandEntity executionCommand;
@ManyToOne(cascade = {CascadeType.MERGE})
@JoinColumns({@JoinColumn(name = "request_id", referencedColumnName = "request_id", nullable = false), @JoinColumn(name = "stage_id", referencedColumnName = "stage_id", nullable = false)})
private StageEntity stage;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH})
@JoinColumn(name = "host_id", referencedColumnName = "host_id", nullable = true)
private HostEntity hostEntity;
@OneToOne(mappedBy = "hostRoleCommandEntity", cascade = CascadeType.REMOVE)
private TopologyLogicalTaskEntity topologyLogicalTaskEntity;
@Basic
@Column(name = "is_background", nullable = false)
private short isBackgroundCommand = 0;
public Long getTaskId() {
return taskId;
}
public void setTaskId(Long taskId) {
this.taskId = taskId;
}
public Long getRequestId() {
return requestId;
}
public void setRequestId(Long requestId) {
this.requestId = requestId;
}
public Long getStageId() {
return stageId;
}
public void setStageId(Long stageId) {
this.stageId = stageId;
}
public String getHostName() {
return hostEntity != null ? hostEntity.getHostName() : null;
}
public Long getHostId() {
return hostEntity != null ? hostEntity.getHostId() : null;
}
public Role getRole() {
return Role.valueOf(role);
}
public void setRole(Role role) {
this.role = role.name();
}
public String getEvent() {
return defaultString(event);
}
public void setEvent(String event) {
this.event = event;
}
public Integer getExitcode() {
return exitcode;
}
public void setExitcode(Integer exitcode) {
this.exitcode = exitcode;
}
public HostRoleStatus getStatus() {
return status;
}
public void setStatus(HostRoleStatus status) {
this.status = status;
}
public byte[] getStdError() {
return ArrayUtils.nullToEmpty(stdError);
}
public void setStdError(byte[] stdError) {
this.stdError = stdError;
}
public byte[] getStdOut() {
return ArrayUtils.nullToEmpty(stdOut);
}
public void setStdOut(byte[] stdOut) {
this.stdOut = stdOut;
}
public String getOutputLog() { return outputLog; }
public void setOutputLog(String outputLog) { this.outputLog = outputLog; }
public String getErrorLog() { return errorLog; }
public void setErrorLog(String errorLog) { this.errorLog = errorLog; }
public Long getStartTime() {
return startTime;
}
public void setStartTime(Long startTime) {
this.startTime = startTime;
}
/**
* Get the original time the command was first scheduled on the agent. This value is never overwritten.
* @return Original start time
*/
public Long getOriginalStartTime() {
return originalStartTime;
}
/**
* Set the original start time when the command is first scheduled. This value is never overwritten.
* @param originalStartTime Original start time
*/
public void setOriginalStartTime(Long originalStartTime) {
this.originalStartTime = originalStartTime;
}
public Long getLastAttemptTime() {
return lastAttemptTime;
}
public void setLastAttemptTime(Long lastAttemptTime) {
this.lastAttemptTime = lastAttemptTime;
}
public Short getAttemptCount() {
return attemptCount;
}
public void setAttemptCount(Short attemptCount) {
this.attemptCount = attemptCount;
}
public RoleCommand getRoleCommand() {
return roleCommand;
}
public void setRoleCommand(RoleCommand roleCommand) {
this.roleCommand = roleCommand;
}
public byte[] getStructuredOut() {
return structuredOut;
}
public void setStructuredOut(byte[] structuredOut) {
this.structuredOut = structuredOut;
}
public Long getEndTime() {
return endTime;
}
public void setEndTime(Long endTime) {
this.endTime = endTime;
}
public String getCommandDetail() {
return commandDetail;
}
public void setCommandDetail(String commandDetail) {
String truncatedCommandDetail = commandDetail;
if (commandDetail != null) {
if (commandDetail.length() > MAX_COMMAND_DETAIL_LENGTH) {
truncatedCommandDetail = commandDetail.substring(0, MAX_COMMAND_DETAIL_LENGTH) + "...";
}
}
this.commandDetail = truncatedCommandDetail;
}
public String getCustomCommandName() {
return customCommandName;
}
public void setCustomCommandName(String customCommandName) {
this.customCommandName = customCommandName;
}
public String getOpsDisplayName() {
return opsDisplayName;
}
public void setOpsDisplayName(String opsDisplayName) {
this.opsDisplayName = opsDisplayName;
}
/**
* Determine whether this task should hold for retry when an error occurs.
*
* @return {@code true} if this task should hold for retry when an error occurs
*/
public boolean isRetryAllowed() {
return retryAllowed != 0;
}
/**
* Sets whether this task should hold for retry when an error occurs.
*
* @param enabled {@code true} if this task should hold for retry when an error occurs
*/
public void setRetryAllowed(boolean enabled) {
retryAllowed = enabled ? 1 : 0;
}
/**
* Gets whether commands which fail and are retryable are automatically
* skipped and marked with {@link HostRoleStatus#SKIPPED_FAILED}.
*
* @return
*/
public boolean isFailureAutoSkipped() {
return autoSkipOnFailure != 0;
}
/**
* Sets whether commands which fail and are retryable are automatically
* skipped and marked with {@link HostRoleStatus#SKIPPED_FAILED}.
*
* @param skipFailures
*/
public void setAutoSkipOnFailure(boolean skipFailures) {
autoSkipOnFailure = skipFailures ? 1 : 0;
}
/**
* Sets whether this is a command is a background command and will not block
* other commands.
*
* @param runInBackground
* {@code true} if this is a background command, {@code false}
* otherwise.
*/
public void setBackgroundCommand(boolean runInBackground) {
isBackgroundCommand = (short) (runInBackground ? 1 : 0);
}
/**
* Gets whether this command runs in the background and will not block other
* commands.
*/
public boolean isBackgroundCommand() {
return isBackgroundCommand == 0 ? false : true;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
HostRoleCommandEntity that = (HostRoleCommandEntity) o;
if (attemptCount != null ? !attemptCount.equals(that.attemptCount) : that.attemptCount != null) {
return false;
}
if (event != null ? !event.equals(that.event) : that.event != null) {
return false;
}
if (exitcode != null ? !exitcode.equals(that.exitcode) : that.exitcode != null) {
return false;
}
if (hostEntity != null ? !hostEntity.equals(that.hostEntity) : that.hostEntity != null) {
return false;
}
if (lastAttemptTime != null ? !lastAttemptTime.equals(that.lastAttemptTime) : that.lastAttemptTime != null) {
return false;
}
if (requestId != null ? !requestId.equals(that.requestId) : that.requestId != null) {
return false;
}
if (role != null ? !role.equals(that.role) : that.role != null) {
return false;
}
if (stageId != null ? !stageId.equals(that.stageId) : that.stageId != null) {
return false;
}
if (startTime != null ? !startTime.equals(that.startTime) : that.startTime != null) {
return false;
}
if (originalStartTime != null ? !originalStartTime.equals(that.originalStartTime) : that.originalStartTime != null) {
return false;
}
if (status != null ? !status.equals(that.status) : that.status != null) {
return false;
}
if (stdError != null ? !Arrays.equals(stdError, that.stdError) : that.stdError != null) {
return false;
}
if (stdOut != null ? !Arrays.equals(stdOut, that.stdOut) : that.stdOut != null) {
return false;
}
if (outputLog != null ? !outputLog.equals(that.outputLog) : that.outputLog != null) {
return false;
}
if (errorLog != null ? !errorLog.equals(that.errorLog) : that.errorLog != null) {
return false;
}
if (taskId != null ? !taskId.equals(that.taskId) : that.taskId != null) {
return false;
}
if (structuredOut != null ? !Arrays.equals(structuredOut, that.structuredOut) : that.structuredOut != null) {
return false;
}
if (endTime != null ? !endTime.equals(that.endTime) : that.endTime != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = taskId != null ? taskId.hashCode() : 0;
result = 31 * result + (requestId != null ? requestId.hashCode() : 0);
result = 31 * result + (stageId != null ? stageId.hashCode() : 0);
result = 31 * result + (hostEntity != null ? hostEntity.hashCode() : 0);
result = 31 * result + (role != null ? role.hashCode() : 0);
result = 31 * result + (event != null ? event.hashCode() : 0);
result = 31 * result + (exitcode != null ? exitcode.hashCode() : 0);
result = 31 * result + (status != null ? status.hashCode() : 0);
result = 31 * result + (stdError != null ? Arrays.hashCode(stdError) : 0);
result = 31 * result + (stdOut != null ? Arrays.hashCode(stdOut) : 0);
result = 31 * result + (outputLog != null ? outputLog.hashCode() : 0);
result = 31 * result + (errorLog != null ? errorLog.hashCode() : 0);
result = 31 * result + (startTime != null ? startTime.hashCode() : 0);
result = 31 * result + (originalStartTime != null ? originalStartTime.hashCode() : 0);
result = 31 * result + (lastAttemptTime != null ? lastAttemptTime.hashCode() : 0);
result = 31 * result + (attemptCount != null ? attemptCount.hashCode() : 0);
result = 31 * result + (endTime != null ? endTime.hashCode() : 0);
result = 31 * result + (structuredOut != null ? Arrays.hashCode(structuredOut) : 0);
return result;
}
public ExecutionCommandEntity getExecutionCommand() {
return executionCommand;
}
public void setExecutionCommand(ExecutionCommandEntity executionCommandsByTaskId) {
executionCommand = executionCommandsByTaskId;
}
public StageEntity getStage() {
return stage;
}
/**
* Sets the associated {@link StageEntity} for this command. If the
* {@link StageEntity} has been persisted, then this will also set the
* commands stage and request ID fields.
*
* @param stage
*/
public void setStage(StageEntity stage) {
this.stage = stage;
// ensure that the IDs are also set since they may not be retrieved from JPA
// when this entity is cached
if (null != stage) {
if (null == stageId) {
stageId = stage.getStageId();
}
if (null == requestId) {
requestId = stage.getRequestId();
}
}
}
public HostEntity getHostEntity() {
return hostEntity;
}
public void setHostEntity(HostEntity hostEntity) {
this.hostEntity = hostEntity;
}
public TopologyLogicalTaskEntity getTopologyLogicalTaskEntity() {
return topologyLogicalTaskEntity;
}
public void setTopologyLogicalTaskEntity(TopologyLogicalTaskEntity topologyLogicalTaskEntity) {
this.topologyLogicalTaskEntity = topologyLogicalTaskEntity;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder buffer = new StringBuilder("HostRoleCommandEntity{ ");
buffer.append("taskId").append(taskId);
buffer.append(", stageId=").append(stageId);
buffer.append(", requestId=").append(requestId);
buffer.append(", role=").append(role);
buffer.append(", roleCommand=").append(roleCommand);
buffer.append(", exitcode=").append(exitcode);
buffer.append("}");
return buffer.toString();
}
}