package io.cattle.platform.process.agent;
import static io.cattle.platform.core.model.tables.InstanceTable.*;
import io.cattle.platform.agent.AgentLocator;
import io.cattle.platform.agent.RemoteAgent;
import io.cattle.platform.agent.util.AgentUtils;
import io.cattle.platform.archaius.util.ArchaiusUtil;
import io.cattle.platform.async.utils.AsyncUtils;
import io.cattle.platform.async.utils.TimeoutException;
import io.cattle.platform.core.constants.AgentConstants;
import io.cattle.platform.core.model.Agent;
import io.cattle.platform.core.model.Instance;
import io.cattle.platform.engine.handler.HandlerResult;
import io.cattle.platform.engine.process.ProcessInstance;
import io.cattle.platform.engine.process.ProcessState;
import io.cattle.platform.eventing.EventCallOptions;
import io.cattle.platform.eventing.EventService;
import io.cattle.platform.eventing.model.Event;
import io.cattle.platform.eventing.model.EventVO;
import io.cattle.platform.framework.event.Ping;
import io.cattle.platform.object.util.DataAccessor;
import io.cattle.platform.process.base.AbstractDefaultProcessHandler;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.cloudstack.managed.context.NoExceptionRunnable;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.netflix.config.DynamicIntProperty;
import com.netflix.config.DynamicLongProperty;
@Named
public class AgentActivate extends AbstractDefaultProcessHandler {
private static final DynamicIntProperty PING_RETRY = ArchaiusUtil.getInt("agent.activate.ping.retries");
private static final DynamicLongProperty PING_TIMEOUT = ArchaiusUtil.getLong("agent.activate.ping.timeout");
private static final DynamicLongProperty PING_DISCONNECT_TIMEOUT = ArchaiusUtil.getLong("agent.disconnect.after.seconds");
@Inject
AgentLocator agentLocator;
@Inject
EventService eventService;
protected HandlerResult checkDisconnect(ProcessState state) {
DataAccessor acc = DataAccessor.fromMap(state.getData()).withScope(AgentActivate.class).withKey("start");
Long startTime = acc.as(Long.class);
if (startTime == null) {
startTime = System.currentTimeMillis();
acc.set(startTime);
}
if (PING_DISCONNECT_TIMEOUT.get() * 1000L < (System.currentTimeMillis() - startTime)) {
return new HandlerResult().withChainProcessName(AgentConstants.PROCESS_DECONNECT).withShouldContinue(false);
}
return null;
}
@Override
public HandlerResult handle(ProcessState state, ProcessInstance process) {
/* This will save the time */
checkDisconnect(state);
Agent agent = (Agent) state.getResource();
Instance instance = objectManager.findAny(Instance.class, INSTANCE.AGENT_ID, agent.getId());
/* Don't ping non-system container agent instances */
if (instance != null) {
return null;
}
for (String prefix : AgentConstants.AGENT_IGNORE_PREFIXES) {
if (agent.getUri() == null || agent.getUri().startsWith(prefix)) {
return new HandlerResult();
}
}
boolean waitFor = DataAccessor.fromDataFieldOf(agent)
.withScope(AgentActivate.class)
.withKey("waitForPing")
.withDefault(process.getName().equals(AgentConstants.PROCESS_RECONNECT))
.as(Boolean.class);
RemoteAgent remoteAgent = agentLocator.lookupAgent(agent);
final ListenableFuture<? extends Event> future = remoteAgent.call(AgentUtils.newPing(agent)
.withOption(Ping.STATS, true)
.withOption(Ping.RESOURCES, true), new EventCallOptions(PING_RETRY.get(), PING_TIMEOUT.get()));
future.addListener(new NoExceptionRunnable() {
@Override
protected void doRun() {
try {
Event resp = future.get();
EventVO<?> respCopy = new EventVO<>(resp);
respCopy.setName("ping.reply");
eventService.publish(respCopy);
} catch (Exception e) {
}
}
}, MoreExecutors.sameThreadExecutor());
if (waitFor) {
try {
AsyncUtils.get(future);
} catch (TimeoutException e) {
HandlerResult result = checkDisconnect(state);
if (result == null) {
throw e;
} else {
return result;
}
}
}
HandlerResult result = new HandlerResult();
if (process.getName().equalsIgnoreCase(AgentConstants.PROCESS_RECONNECT)) {
result.shouldDelegate(true);
}
return result;
}
@Override
public String[] getProcessNames() {
return new String[] { AgentConstants.PROCESS_ACTIVATE, AgentConstants.PROCESS_RECONNECT };
}
public AgentLocator getAgentLocator() {
return agentLocator;
}
@Inject
public void setAgentLocator(AgentLocator agentLocator) {
this.agentLocator = agentLocator;
}
}