package org.zstack.compute.vm;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.db.Q;
import org.zstack.core.gc.EventBasedGarbageCollector;
import org.zstack.core.gc.GC;
import org.zstack.core.gc.GCCompletion;
import org.zstack.core.workflow.FlowChainBuilder;
import org.zstack.header.core.workflow.*;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.host.HostCanonicalEvents;
import org.zstack.header.host.HostConstant;
import org.zstack.header.host.HostStatus;
import org.zstack.header.host.HostVO;
import org.zstack.header.message.MessageReply;
import org.zstack.header.vm.*;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import java.util.Map;
/**
* Created by xing5 on 2017/3/4.
*/
public class StopVmGC extends EventBasedGarbageCollector {
private static final CLogger logger = Utils.getLogger(StopVmGC.class);
@GC
public String hostUuid;
@GC
public VmInstanceInventory inventory;
@Override
protected void triggerNow(GCCompletion completion) {
if (!dbf.isExist(hostUuid, HostVO.class)) {
completion.cancel();
return;
}
VmInstanceState state = Q.New(VmInstanceVO.class).select(VmInstanceVO_.state)
.eq(VmInstanceVO_.uuid, inventory.getUuid()).findValue();
if (state == null || state == VmInstanceState.Destroyed) {
completion.cancel();
return;
}
FlowChain chain = FlowChainBuilder.newSimpleFlowChain();
chain.setName(String.format("gc-stop-vm-%s-on-host-%s", inventory.getUuid(), hostUuid));
chain.then(new NoRollbackFlow() {
@Override
public void run(FlowTrigger trigger, Map data) {
StopVmOnHypervisorMsg msg = new StopVmOnHypervisorMsg();
msg.setVmInventory(inventory);
bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, hostUuid);
bus.send(msg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
trigger.fail(reply.getError());
} else {
trigger.next();
}
}
});
}
}).then(new NoRollbackFlow() {
@Override
public void run(FlowTrigger trigger, Map data) {
ChangeVmStateMsg cmsg = new ChangeVmStateMsg();
cmsg.setVmInstanceUuid(inventory.getUuid());
cmsg.setStateEvent(VmInstanceStateEvent.stopped.toString());
bus.makeTargetServiceIdByResourceUuid(cmsg, VmInstanceConstant.SERVICE_ID, inventory.getUuid());
bus.send(cmsg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
logger.warn(String.format("failed to change vm[uuid:%s,name:%s]'s status, however, it has been" +
" stopped on the host[uuid:%s]", inventory.getUuid(), inventory.getName(), hostUuid));
trigger.next();
}
});
}
}).done(new FlowDoneHandler(completion) {
@Override
public void handle(Map data) {
completion.success();
}
}).error(new FlowErrorHandler(completion) {
@Override
public void handle(ErrorCode errCode, Map data) {
completion.fail(errCode);
}
}).start();
}
@Override
protected void setup() {
onEvent(HostCanonicalEvents.HOST_STATUS_CHANGED_PATH, (tokens, data) -> {
HostCanonicalEvents.HostStatusChangedData d = (HostCanonicalEvents.HostStatusChangedData) data;
return d.getHostUuid().equals(hostUuid) && d.getNewStatus().equals(HostStatus.Connected.toString());
});
onEvent(HostCanonicalEvents.HOST_DELETED_PATH, ((tokens, data) -> {
HostCanonicalEvents.HostDeletedData d = (HostCanonicalEvents.HostDeletedData) data;
return hostUuid.equals(d.getHostUuid());
}));
onEvent(VmCanonicalEvents.VM_FULL_STATE_CHANGED_PATH, ((tokens, data) -> {
VmCanonicalEvents.VmStateChangedData d = (VmCanonicalEvents.VmStateChangedData) data;
return d.getVmUuid().equals(inventory.getUuid()) && d.getNewState().equals(VmInstanceState.Destroyed.toString());
}));
}
}