package com.intrbiz.bergamot.model.state;
import java.sql.Timestamp;
import java.util.EnumSet;
import java.util.UUID;
import com.intrbiz.Util;
import com.intrbiz.bergamot.config.model.CheckCfg;
import com.intrbiz.bergamot.config.model.RealCheckCfg;
import com.intrbiz.bergamot.data.BergamotDB;
import com.intrbiz.bergamot.model.Alert;
import com.intrbiz.bergamot.model.BergamotObject;
import com.intrbiz.bergamot.model.Contact;
import com.intrbiz.bergamot.model.Status;
import com.intrbiz.bergamot.model.message.state.CheckStateMO;
import com.intrbiz.data.db.compiler.meta.SQLColumn;
import com.intrbiz.data.db.compiler.meta.SQLPrimaryKey;
import com.intrbiz.data.db.compiler.meta.SQLTable;
import com.intrbiz.data.db.compiler.meta.SQLVersion;
/**
* The current state of a check
*
* This is always the most current state of a check
*
*/
@SQLTable(schema = BergamotDB.class, name = "check_state", since = @SQLVersion({ 1, 0, 0 }))
public class CheckState extends BergamotObject<CheckStateMO> implements Cloneable
{
private static final long serialVersionUID = 1L;
@SQLColumn(index = 1, name = "check_id", since = @SQLVersion({ 1, 0, 0 }))
@SQLPrimaryKey
private UUID checkId;
/**
* Is the check ok?
*/
@SQLColumn(index = 2, name = "ok", since = @SQLVersion({ 1, 0, 0 }))
private boolean ok = true;
/**
* Why is the check ok or not ok?
*/
@SQLColumn(index = 3, name = "status", since = @SQLVersion({ 1, 0, 0 }))
private Status status = Status.PENDING;
/**
* What was the output of the last check
*/
@SQLColumn(index = 4, name = "output", since = @SQLVersion({ 1, 0, 0 }))
private String output = "Pending";
/**
* When did the last check happen
*/
@SQLColumn(index = 5, name = "last_check_time", since = @SQLVersion({ 1, 0, 0 }))
private Timestamp lastCheckTime = new Timestamp(System.currentTimeMillis());
/**
* What was the Id of the last check
*/
@SQLColumn(index = 6, name = "last_check_id", since = @SQLVersion({ 1, 0, 0 }))
private UUID lastCheckId;
/**
* The number of attempts since the last hard state change
*/
@SQLColumn(index = 7, name = "attempt", since = @SQLVersion({ 1, 0, 0 }))
private int attempt = 1;
/**
* Has a hard state transition happened
*/
@SQLColumn(index = 8, name = "hard", since = @SQLVersion({ 1, 0, 0 }))
private boolean hard = true;
/**
* Is the state in transition
*/
@SQLColumn(index = 9, name = "transitioning", since = @SQLVersion({ 1, 0, 0 }))
private boolean transitioning = false;
/**
* Is the state flapping between ok and not ok, but never reaching a hard state
*/
@SQLColumn(index = 10, name = "flapping", since = @SQLVersion({ 1, 0, 0 }))
private boolean flapping = false;
/**
* When was the last hard state change
*/
@SQLColumn(index = 11, name = "last_state_change", since = @SQLVersion({ 1, 0, 0 }))
private Timestamp lastStateChange = new Timestamp(System.currentTimeMillis());
// history
/**
* A bitmap of the ok history
*/
@SQLColumn(index = 12, name = "ok_history", since = @SQLVersion({ 1, 0, 0 }))
private long okHistory = 0x1L;
/**
* Was the last hard state ok?
*/
@SQLColumn(index = 13, name = "last_hard_ok", since = @SQLVersion({ 1, 0, 0 }))
private boolean lastHardOk = true;
/**
* What was the last hard status?
*/
@SQLColumn(index = 14, name = "last_hard_status", since = @SQLVersion({ 1, 0, 0 }))
private Status lastHardStatus = Status.PENDING;
/**
* What was the output of the last hard state
*/
@SQLColumn(index = 15, name = "last_hard_output", since = @SQLVersion({ 1, 0, 0 }))
private String lastHardOutput = "Pending";
/**
* Is this check currently in downtime
*/
@SQLColumn(index = 16, name = "in_downtime", since = @SQLVersion({ 3, 3, 0 }))
private boolean inDowntime;
/**
* Is this check currently suppressed
*/
@SQLColumn(index = 17, name = "suppressed", since = @SQLVersion({ 3, 4, 0 }))
private boolean suppressed;
/**
* Is this check currently acknowledged
*/
@SQLColumn(index = 18, name = "acknowledged", since = @SQLVersion({ 3, 28, 0 }))
private boolean acknowledged;
/**
* Is this check currently encompassed by an alert on a dependent check
*/
@SQLColumn(index = 19, name = "encompassed", since = @SQLVersion({ 3, 28, 0 }))
private boolean encompassed;
/**
* The current alert id for this check
*/
@SQLColumn(index = 20, name = "current_alert_id", since = @SQLVersion({ 3, 28, 0 }))
private UUID currentAlertId;
public CheckState()
{
super();
}
public UUID getCheckId()
{
return checkId;
}
public void setCheckId(UUID checkId)
{
this.checkId = checkId;
}
public boolean isOk()
{
return ok;
}
public void setOk(boolean ok)
{
this.ok = ok;
}
public Status getStatus()
{
return status;
}
public void setStatus(Status status)
{
this.status = status;
}
public String getOutput()
{
return output;
}
public void setOutput(String output)
{
this.output = output;
}
public Timestamp getLastCheckTime()
{
return lastCheckTime;
}
public void setLastCheckTime(Timestamp lastCheckTime)
{
this.lastCheckTime = lastCheckTime;
}
public UUID getLastCheckId()
{
return lastCheckId;
}
public void setLastCheckId(UUID lastCheckId)
{
this.lastCheckId = lastCheckId;
}
public int getAttempt()
{
return attempt;
}
public void setAttempt(int attempt)
{
this.attempt = attempt;
}
/**
* Are we in a hard state
*/
public boolean isHard()
{
return hard;
}
/**
* Are we in a soft state, the opposite of a hard state
*/
public boolean isSoft()
{
return ! hard;
}
/**
* Are we in a hard OK state, IE: this.hard && this.ok
*/
public boolean isHardOk()
{
return this.hard && this.ok;
}
/**
* Are we in a hard not OK state, IE: this.hard && ! this.ok
*/
public boolean isHardNotOk()
{
return this.hard && (! this.ok);
}
public void setHard(boolean hard)
{
this.hard = hard;
}
public Timestamp getLastStateChange()
{
return lastStateChange;
}
public void setLastStateChange(Timestamp lastStateChange)
{
this.lastStateChange = lastStateChange;
}
public long getOkHistory()
{
return okHistory;
}
public void setOkHistory(long okHistory)
{
this.okHistory = okHistory;
}
public void pushOkHistory(boolean ok)
{
this.okHistory = ((this.okHistory << 1) & 0x7FFFFFFFFFFFFFFFL) | (ok ? 1L : 0L);
}
public boolean isTransitioning()
{
return transitioning;
}
public void setTransitioning(boolean transitioning)
{
this.transitioning = transitioning;
}
public boolean isFlapping()
{
return flapping;
}
public void setFlapping(boolean flapping)
{
this.flapping = flapping;
}
public boolean isLastHardOk()
{
return lastHardOk;
}
public void setLastHardOk(boolean lastHardOk)
{
this.lastHardOk = lastHardOk;
}
public Status getLastHardStatus()
{
return lastHardStatus;
}
public void setLastHardStatus(Status lastHardStatus)
{
this.lastHardStatus = lastHardStatus;
}
public String getLastHardOutput()
{
return lastHardOutput;
}
public void setLastHardOutput(String lastHardOutput)
{
this.lastHardOutput = lastHardOutput;
}
/**
* Is this check currently in downtime
*/
public boolean isInDowntime()
{
return this.inDowntime;
}
public void setInDowntime(boolean inDowntime)
{
this.inDowntime = inDowntime;
}
public boolean isSuppressed()
{
return suppressed;
}
public void setSuppressed(boolean suppressed)
{
this.suppressed = suppressed;
}
public boolean isSuppressedOrInDowntime()
{
return this.suppressed || this.inDowntime;
}
/**
* Should this check currently be ignore.
* This means that the check is currently
* suppressed, in downtime, acknowledged
* or encompassed.
* @return true if this check should be ignored
*/
public boolean isIgnored()
{
return this.suppressed || this.inDowntime || this.acknowledged || this.encompassed;
}
public boolean isAcknowledged()
{
return acknowledged;
}
public void setAcknowledged(boolean acknowledged)
{
this.acknowledged = acknowledged;
}
public boolean isEncompassed()
{
return encompassed;
}
public void setEncompassed(boolean encompassed)
{
this.encompassed = encompassed;
}
public UUID getCurrentAlertId()
{
return currentAlertId;
}
public void setCurrentAlertId(UUID currentAlertId)
{
this.currentAlertId = currentAlertId;
}
public Alert getCurrentAlert()
{
if (this.getCurrentAlertId() != null)
{
try (BergamotDB db = BergamotDB.connect())
{
return db.getAlert(this.getCurrentAlertId());
}
}
return null;
}
/**
* Does this state represent an alert
* @return true if this state is an alert
*/
public boolean isAlert()
{
return (this.isOk() ^ this.isLastHardOk())
&& (! this.isOk())
&& (! this.isSuppressedOrInDowntime());
}
/**
* Does this state represent a recovery
* @return true if this state is a recovery
*/
public boolean isRecovery()
{
return (this.isOk() ^ this.isLastHardOk())
&& this.isOk()
&& (! this.isSuppressedOrInDowntime());
}
@Override
public CheckStateMO toMO(Contact contact, EnumSet<MOFlag> options)
{
CheckStateMO mo = new CheckStateMO();
mo.setAttempt(this.getAttempt());
mo.setFlapping(this.isFlapping());
mo.setHard(this.isHard());
mo.setLastCheckId(this.getLastCheckId());
mo.setLastCheckTime(this.getLastCheckTime().getTime());
mo.setLastStateChange(this.getLastStateChange().getTime());
mo.setOk(this.isOk());
mo.setOutput(this.getOutput());
mo.setStatus(this.getStatus().toString());
mo.setTransitioning(this.isTransitioning());
mo.setLastHardOk(this.isLastHardOk());
mo.setLastHardStatus(this.getLastHardStatus().toString());
mo.setLastHardOutput(this.getLastHardOutput());
mo.setInDowntime(this.isInDowntime());
mo.setSuppressed(this.isSuppressed());
mo.setAcknowledged(this.isAcknowledged());
mo.setEncompassed(this.isEncompassed());
mo.setCurrentAlert(this.getCurrentAlertId());
return mo;
}
public void configure(CheckCfg<?> cfg)
{
if (cfg.getInitialState() != null)
{
this.setStatus(Status.valueOf(cfg.getInitialState().getStatus().toUpperCase()));
this.setOk(this.getStatus().isOk());
this.setOutput(Util.coalesce(cfg.getInitialState().getOutput(), ""));
this.setLastHardStatus(this.getStatus());
this.setLastHardOk(this.isOk());
this.setLastHardOutput(this.getOutput());
}
// set the attempt
if (cfg instanceof RealCheckCfg && ((RealCheckCfg<?>)cfg).getState() != null)
{
this.setAttempt(((RealCheckCfg<?>)cfg).getState().getRecoversAfter());
}
// update last check time
this.setLastCheckTime(new Timestamp(System.currentTimeMillis()));
}
public String toString()
{
return "CheckState { check => " + this.checkId + ", ok => " + this.ok + ", status => " + this.status + ", output => " + this.output + ", attempt => " + this.attempt + ", hard => " + this.hard + " }";
}
public CheckState clone()
{
try
{
return (CheckState) super.clone();
}
catch (CloneNotSupportedException e)
{
throw new RuntimeException(e);
}
}
}