package com.sequenceiq.cloudbreak.domain;
import static com.sequenceiq.cloudbreak.api.model.Status.AVAILABLE;
import static com.sequenceiq.cloudbreak.api.model.Status.CREATE_IN_PROGRESS;
import static com.sequenceiq.cloudbreak.api.model.Status.DELETE_COMPLETED;
import static com.sequenceiq.cloudbreak.api.model.Status.DELETE_IN_PROGRESS;
import static com.sequenceiq.cloudbreak.api.model.Status.REQUESTED;
import static com.sequenceiq.cloudbreak.api.model.Status.START_FAILED;
import static com.sequenceiq.cloudbreak.api.model.Status.START_IN_PROGRESS;
import static com.sequenceiq.cloudbreak.api.model.Status.START_REQUESTED;
import static com.sequenceiq.cloudbreak.api.model.Status.STOPPED;
import static com.sequenceiq.cloudbreak.api.model.Status.STOP_FAILED;
import static com.sequenceiq.cloudbreak.api.model.Status.STOP_IN_PROGRESS;
import static com.sequenceiq.cloudbreak.api.model.Status.STOP_REQUESTED;
import static com.sequenceiq.cloudbreak.api.model.Status.UPDATE_IN_PROGRESS;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.ElementCollection;
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.ManyToOne;
import javax.persistence.MapKeyColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.persistence.Version;
import com.sequenceiq.cloudbreak.api.model.InstanceGroupType;
import com.sequenceiq.cloudbreak.api.model.InstanceMetadataType;
import com.sequenceiq.cloudbreak.api.model.OnFailureAction;
import com.sequenceiq.cloudbreak.api.model.Status;
import com.sequenceiq.cloudbreak.common.type.ResourceType;
import com.sequenceiq.cloudbreak.domain.json.Json;
import com.sequenceiq.cloudbreak.domain.json.JsonToString;
@Entity
@Table(name = "Stack", uniqueConstraints = {
@UniqueConstraint(columnNames = {"account", "name"})
})
@NamedQueries({
@NamedQuery(
name = "Stack.findById",
query = "SELECT c FROM Stack c "
+ "LEFT JOIN FETCH c.resources "
+ "LEFT JOIN FETCH c.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE c.id= :id"),
@NamedQuery(
name = "Stack.findByIdLazy",
query = "SELECT c FROM Stack c "
+ "WHERE c.id= :id"),
@NamedQuery(
name = "Stack.findByIdWithSecurityConfig",
query = "SELECT s FROM Stack s "
+ "LEFT JOIN FETCH s.securityConfig "
+ "WHERE s.id= :id"),
@NamedQuery(
name = "Stack.findAllStackForTemplate",
query = "SELECT distinct c FROM Stack c "
+ "LEFT JOIN FETCH c.instanceGroups ig "
+ "WHERE ig.template.id= :id"),
@NamedQuery(
name = "Stack.findStackForCluster",
query = "SELECT c FROM Stack c "
+ "LEFT JOIN FETCH c.resources "
+ "LEFT JOIN FETCH c.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE c.cluster.id= :id"),
@NamedQuery(
name = "Stack.findStackWithListsForCluster",
query = "SELECT c FROM Stack c "
+ "LEFT JOIN FETCH c.resources "
+ "LEFT JOIN FETCH c.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE c.cluster.id= :id"),
@NamedQuery(
name = "Stack.findRequestedStacksWithCredential",
query = "SELECT DISTINCT c FROM Stack c "
+ "LEFT JOIN FETCH c.resources "
+ "LEFT JOIN FETCH c.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE c.credential.id= :credentialId "
+ "AND c.stackStatus.status= 'REQUESTED'"),
@NamedQuery(
name = "Stack.findOneWithLists",
query = "SELECT c FROM Stack c "
+ "LEFT JOIN FETCH c.resources "
+ "LEFT JOIN FETCH c.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE c.id= :id"),
@NamedQuery(
name = "Stack.findByStackResourceName",
query = "SELECT c FROM Stack c inner join c.resources res "
+ "WHERE res.resourceName = :stackName AND res.resourceType = 'CLOUDFORMATION_STACK'"),
@NamedQuery(
name = "Stack.findForUser",
query = "SELECT s FROM Stack s "
+ "LEFT JOIN FETCH s.resources "
+ "LEFT JOIN FETCH s.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE s.owner= :user "
+ "AND s.stackStatus.status <> 'DELETE_COMPLETED' "),
@NamedQuery(
name = "Stack.findPublicInAccountForUser",
query = "SELECT s FROM Stack s "
+ "LEFT JOIN FETCH s.resources "
+ "LEFT JOIN FETCH s.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "LEFT JOIN FETCH s.cluster c "
+ "LEFT JOIN FETCH c.hostGroups "
+ "WHERE ((s.account= :account AND s.publicInAccount= true) OR s.owner= :user) "
+ "AND s.stackStatus.status <> 'DELETE_COMPLETED' "),
@NamedQuery(
name = "Stack.findAllInAccount",
query = "SELECT s FROM Stack s "
+ "LEFT JOIN FETCH s.resources "
+ "LEFT JOIN FETCH s.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "LEFT JOIN FETCH s.cluster c "
+ "LEFT JOIN FETCH c.hostGroups "
+ "WHERE s.account= :account "
+ "AND s.stackStatus.status <> 'DELETE_COMPLETED' "),
@NamedQuery(
name = "Stack.findByAmbari",
query = "SELECT s from Stack s "
+ "LEFT JOIN FETCH s.resources "
+ "LEFT JOIN FETCH s.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE s.cluster.ambariIp= :ambariIp "
+ "AND s.stackStatus.status <> 'DELETE_COMPLETED' "),
@NamedQuery(
name = "Stack.findOneByName",
query = "SELECT c FROM Stack c "
+ "LEFT JOIN FETCH c.resources "
+ "LEFT JOIN FETCH c.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE c.name= :name and c.account= :account"),
@NamedQuery(
name = "Stack.findByIdInAccount",
query = "SELECT s FROM Stack s "
+ "LEFT JOIN FETCH s.resources "
+ "LEFT JOIN FETCH s.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE s.id= :id and s.account= :account"),
@NamedQuery(
name = "Stack.findByNameInAccount",
query = "SELECT s FROM Stack s "
+ "LEFT JOIN FETCH s.resources "
+ "LEFT JOIN FETCH s.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE s.name= :name and ((s.account= :account and s.publicInAccount=true) or s.owner= :owner)"),
@NamedQuery(
name = "Stack.findByNameInUser",
query = "SELECT t FROM Stack t "
+ "LEFT JOIN FETCH t.resources "
+ "LEFT JOIN FETCH t.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE t.owner= :owner and t.name= :name"),
@NamedQuery(
name = "Stack.findByCredential",
query = "SELECT t FROM Stack t "
+ "LEFT JOIN FETCH t.resources "
+ "LEFT JOIN FETCH t.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE t.credential.id= :credentialId "),
@NamedQuery(
name = "Stack.findAllByNetwork",
query = "SELECT t FROM Stack t "
+ "LEFT JOIN FETCH t.resources "
+ "LEFT JOIN FETCH t.instanceGroups ig "
+ "LEFT JOIN FETCH ig.instanceMetaData "
+ "WHERE t.network.id= :networkId "),
@NamedQuery(
name = "Stack.findAllAlive",
query = "SELECT s FROM Stack s "
+ "WHERE s.stackStatus.status <> 'DELETE_COMPLETED' "),
@NamedQuery(
name = "Stack.findAllAliveAndProvisioned",
query = "SELECT s FROM Stack s "
+ "WHERE s.stackStatus.status <> 'DELETE_COMPLETED' "
+ "AND s.stackStatus.status <> 'REQUESTED' "
+ "AND s.stackStatus.status <> 'CREATE_IN_PROGRESS' "),
@NamedQuery(
name = "Stack.findAliveOnes",
query = "SELECT s FROM Stack s "
+ "LEFT JOIN FETCH s.cluster "
+ "LEFT JOIN FETCH s.credential "
+ "LEFT JOIN FETCH s.network "
+ "LEFT JOIN FETCH s.orchestrator "
+ "LEFT JOIN FETCH s.stackStatus "
+ "LEFT JOIN FETCH s.securityConfig "
+ "LEFT JOIN FETCH s.failurePolicy "
+ "WHERE s.stackStatus.status <> 'DELETE_COMPLETED' "
+ "AND s.stackStatus.status <> 'DELETE_IN_PROGRESS' "),
@NamedQuery(
name = "Stack.findByStatuses",
query = "SELECT s FROM Stack s "
+ "WHERE s.stackStatus.status IN :statuses"
),
@NamedQuery(
name = "Stack.findStacksWithoutEvents",
query = "SELECT s.id FROM Stack s "
+ "WHERE s.id NOT IN (SELECT DISTINCT e.stackId FROM CloudbreakEvent e)"
)
})
public class Stack implements ProvisionEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "stack_generator")
@SequenceGenerator(name = "stack_generator", sequenceName = "stack_id_seq", allocationSize = 1)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String owner;
@Column(nullable = false)
private String account;
@Column(nullable = false)
private boolean publicInAccount;
@Column(nullable = false)
private String region;
private String availabilityZone;
private Integer gatewayPort;
private int consulServers;
@Column(length = 1000000, columnDefinition = "TEXT")
private String description;
@ElementCollection(fetch = FetchType.EAGER)
@MapKeyColumn(name = "key")
@Column(name = "value", columnDefinition = "TEXT", length = 100000, nullable = false)
private Map<String, String> parameters;
@OneToOne
private Credential credential;
@Column(columnDefinition = "TEXT")
private String platformVariant;
@Column(columnDefinition = "TEXT")
private String cloudPlatform;
@OneToOne(mappedBy = "stack", cascade = CascadeType.REMOVE, orphanRemoval = true)
private Cluster cluster;
@OneToOne(cascade = CascadeType.ALL)
private StackStatus stackStatus;
@OneToMany(mappedBy = "stack", cascade = CascadeType.REMOVE, orphanRemoval = true)
private Set<Resource> resources = new HashSet<>();
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private OnFailureAction onFailureActionAction = OnFailureAction.ROLLBACK;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
private FailurePolicy failurePolicy;
@OneToOne(mappedBy = "stack", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY)
private SecurityConfig securityConfig;
@OneToMany(mappedBy = "stack", cascade = CascadeType.REMOVE, orphanRemoval = true)
private Set<InstanceGroup> instanceGroups = new HashSet<>();
@OneToMany(mappedBy = "stack", cascade = CascadeType.REMOVE, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<Component> components = new HashSet<>();
@Version
private Long version;
@ManyToOne
private Network network;
@OneToOne
private Orchestrator orchestrator;
private Long created;
private Boolean relocateDocker;
@Convert(converter = JsonToString.class)
@Column(columnDefinition = "TEXT")
private Json tags;
@ManyToOne
private FlexSubscription flexSubscription;
private String uuid;
public Set<InstanceGroup> getInstanceGroups() {
return instanceGroups;
}
public void setInstanceGroups(Set<InstanceGroup> instanceGroups) {
this.instanceGroups = instanceGroups;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public boolean isPublicInAccount() {
return publicInAccount;
}
public void setPublicInAccount(boolean publicInAccount) {
this.publicInAccount = publicInAccount;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Cluster getCluster() {
return cluster;
}
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
public Credential getCredential() {
return credential;
}
public void setCredential(Credential credential) {
this.credential = credential;
}
public StackStatus getStackStatus() {
return stackStatus;
}
public void setStackStatus(StackStatus stackStatus) {
this.stackStatus = stackStatus;
}
public Status getStatus() {
return stackStatus != null ? stackStatus.getStatus() : null;
}
public String getStatusReason() {
return stackStatus != null ? stackStatus.getStatusReason() : null;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public Set<Resource> getResources() {
return resources;
}
public void setResources(Set<Resource> resources) {
this.resources = resources;
}
public int getConsulServers() {
return consulServers;
}
public void setConsulServers(int consulServers) {
this.consulServers = consulServers;
}
public OnFailureAction getOnFailureActionAction() {
return onFailureActionAction;
}
public void setOnFailureActionAction(OnFailureAction onFailureActionAction) {
this.onFailureActionAction = onFailureActionAction;
}
public FailurePolicy getFailurePolicy() {
return failurePolicy;
}
public void setFailurePolicy(FailurePolicy failurePolicy) {
this.failurePolicy = failurePolicy;
}
public SecurityConfig getSecurityConfig() {
return securityConfig;
}
public void setSecurityConfig(SecurityConfig securityConfig) {
this.securityConfig = securityConfig;
}
public Orchestrator getOrchestrator() {
return orchestrator;
}
public void setOrchestrator(Orchestrator orchestrator) {
this.orchestrator = orchestrator;
}
public String getAvailabilityZone() {
return availabilityZone;
}
public void setAvailabilityZone(String availabilityZone) {
this.availabilityZone = availabilityZone;
}
public Integer getGatewayPort() {
return gatewayPort;
}
public void setGatewayPort(Integer gatewayPort) {
this.gatewayPort = gatewayPort;
}
public String getPlatformVariant() {
return platformVariant;
}
public void setPlatformVariant(String platformVariant) {
this.platformVariant = platformVariant;
}
public String cloudPlatform() {
return cloudPlatform;
}
public void setCloudPlatform(String cloudPlatform) {
this.cloudPlatform = cloudPlatform;
}
public Boolean getRelocateDocker() {
return relocateDocker;
}
public void setRelocateDocker(Boolean relocateDocker) {
this.relocateDocker = relocateDocker;
}
public List<Resource> getResourcesByType(ResourceType resourceType) {
List<Resource> resourceList = new ArrayList<>();
for (Resource resource : resources) {
if (resourceType.equals(resource.getResourceType())) {
resourceList.add(resource);
}
}
return resourceList;
}
public Resource getResourceByType(ResourceType resourceType) {
for (Resource resource : resources) {
if (resourceType.equals(resource.getResourceType())) {
return resource;
}
}
return null;
}
public InstanceGroup getInstanceGroupByInstanceGroupId(Long groupId) {
for (InstanceGroup instanceGroup : instanceGroups) {
if (groupId.equals(instanceGroup.getId())) {
return instanceGroup;
}
}
return null;
}
public InstanceGroup getInstanceGroupByInstanceGroupName(String group) {
for (InstanceGroup instanceGroup : instanceGroups) {
if (group.equals(instanceGroup.getGroupName())) {
return instanceGroup;
}
}
return null;
}
public Integer getFullNodeCount() {
int nodeCount = 0;
for (InstanceGroup instanceGroup : instanceGroups) {
nodeCount += instanceGroup.getNodeCount();
}
return nodeCount;
}
public Set<InstanceMetaData> getRunningInstanceMetaData() {
Set<InstanceMetaData> instanceMetadata = new HashSet<>();
for (InstanceGroup instanceGroup : instanceGroups) {
instanceMetadata.addAll(instanceGroup.getInstanceMetaData());
}
return instanceMetadata;
}
public List<InstanceMetaData> getInstanceMetaDataAsList() {
return new ArrayList<>(getRunningInstanceMetaData());
}
public List<InstanceGroup> getInstanceGroupsAsList() {
return new ArrayList<>(instanceGroups);
}
public boolean isStackInDeletionPhase() {
return DELETE_COMPLETED.equals(getStatus()) || DELETE_IN_PROGRESS.equals(getStatus());
}
public boolean isStopFailed() {
return STOP_FAILED.equals(getStatus());
}
public boolean isStackInStopPhase() {
return STOP_IN_PROGRESS.equals(getStatus()) || STOPPED.equals(getStatus());
}
public boolean isStartFailed() {
return START_FAILED.equals(getStatus());
}
public Map<String, String> getParameters() {
return parameters;
}
public void setParameters(Map<String, String> parameters) {
this.parameters = parameters;
}
public List<InstanceMetaData> getGatewayInstanceMetadata() {
List<InstanceMetaData> metadataList = new ArrayList<>();
for (InstanceGroup instanceGroup : instanceGroups) {
if (InstanceGroupType.GATEWAY.equals(instanceGroup.getInstanceGroupType())) {
metadataList.addAll(instanceGroup.getInstanceMetaData());
}
}
return metadataList;
}
public InstanceMetaData getPrimaryGatewayInstance() {
Optional<InstanceMetaData> metaData = getGatewayInstanceMetadata().stream()
.filter(im -> InstanceMetadataType.GATEWAY_PRIMARY.equals(im.getInstanceMetadataType())).findFirst();
return metaData.orElse(null);
}
public Network getNetwork() {
return network;
}
public void setNetwork(Network network) {
this.network = network;
}
public String getAmbariIp() {
return cluster == null ? null : cluster.getAmbariIp();
}
public boolean isAvailable() {
return AVAILABLE.equals(getStatus());
}
public boolean isStopRequested() {
return STOP_REQUESTED.equals(getStatus());
}
public boolean isStopped() {
return STOPPED.equals(getStatus());
}
public boolean isDeleteCompleted() {
return DELETE_COMPLETED.equals(getStatus());
}
public boolean isDeleteInProgress() {
return DELETE_IN_PROGRESS.equals(getStatus());
}
public boolean isStartInProgress() {
return START_IN_PROGRESS.equals(getStatus()) || START_REQUESTED.equals(getStatus());
}
public boolean isRequested() {
return REQUESTED.equals(getStatus()) || CREATE_IN_PROGRESS.equals(getStatus());
}
public boolean isStackReadyForStop() {
return AVAILABLE.equals(getStatus()) || STOP_REQUESTED.equals(getStatus());
}
public boolean isModificationInProgress() {
Status status = getStatus();
return CREATE_IN_PROGRESS.equals(status)
|| UPDATE_IN_PROGRESS.equals(status)
|| STOP_IN_PROGRESS.equals(status)
|| START_IN_PROGRESS.equals(status)
|| DELETE_IN_PROGRESS.equals(status);
}
public StopRestrictionReason isInfrastructureStoppable() {
StopRestrictionReason reason = StopRestrictionReason.NONE;
if ("AWS".equals(cloudPlatform())) {
for (InstanceGroup instanceGroup : instanceGroups) {
if ("ephemeral".equals(instanceGroup.getTemplate().getVolumeType())) {
reason = StopRestrictionReason.EPHEMERAL_VOLUMES;
break;
} else {
Json attributes = instanceGroup.getTemplate().getAttributes();
if (attributes != null && attributes.getMap().get("spotPrice") != null) {
reason = StopRestrictionReason.SPOT_INSTANCES;
break;
}
}
}
}
return reason;
}
public Long getCreated() {
return created;
}
public void setCreated(Long created) {
this.created = created;
}
public Set<Component> getComponents() {
return components;
}
public void setComponents(Set<Component> components) {
this.components = components;
}
public boolean isInstanceGroupsSpecified() {
return instanceGroups != null && !instanceGroups.isEmpty();
}
public Json getTags() {
return tags;
}
public void setTags(Json tags) {
this.tags = tags;
}
public FlexSubscription getFlexSubscription() {
return flexSubscription;
}
public void setFlexSubscription(FlexSubscription flexSubscription) {
this.flexSubscription = flexSubscription;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
}