package org.zstack.appliancevm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.util.UriComponentsBuilder;
import org.zstack.appliancevm.ApplianceVmCommands.RefreshFirewallCmd;
import org.zstack.appliancevm.ApplianceVmCommands.RefreshFirewallRsp;
import org.zstack.compute.vm.VmInstanceBase;
import org.zstack.core.CoreGlobalProperty;
import org.zstack.core.MessageCommandRecorder;
import org.zstack.core.db.SimpleQuery;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.header.core.Completion;
import org.zstack.header.core.workflow.*;
import org.zstack.header.errorcode.SysErrors;
import org.zstack.core.thread.ChainTask;
import org.zstack.core.thread.SyncTaskChain;
import org.zstack.header.configuration.*;
import org.zstack.header.core.NoErrorCompletion;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.host.HypervisorType;
import org.zstack.header.image.ImageInventory;
import org.zstack.header.image.ImageVO;
import org.zstack.header.message.Message;
import org.zstack.header.network.l3.L3NetworkInventory;
import org.zstack.header.network.l3.L3NetworkVO;
import org.zstack.header.network.l3.L3NetworkVO_;
import org.zstack.header.rest.JsonAsyncRESTCallback;
import org.zstack.header.rest.RESTFacade;
import org.zstack.header.vm.*;
import org.zstack.header.vm.VmInstanceConstant.VmOperation;
import org.zstack.header.vm.VmInstanceDeletionPolicyManager.VmInstanceDeletionPolicy;
import org.zstack.header.volume.VolumeFormat;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.RangeSet;
import org.zstack.utils.RangeSet.Range;
import org.zstack.utils.function.Function;
import static org.zstack.core.Platform.operr;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static org.zstack.utils.CollectionDSL.list;
public abstract class ApplianceVmBase extends VmInstanceBase implements ApplianceVm {
@Autowired
private RESTFacade restf;
static {
allowedOperations.addState(VmInstanceState.Created, StartNewCreatedApplianceVmMsg.class.getName());
allowedOperations.addState(VmInstanceState.Running, ApplianceVmRefreshFirewallMsg.class.getName());
allowedOperations.addState(VmInstanceState.Starting, ApplianceVmRefreshFirewallMsg.class.getName());
allowedOperations.addState(VmInstanceState.Stopping, ApplianceVmRefreshFirewallMsg.class.getName());
allowedOperations.addState(VmInstanceState.Rebooting, ApplianceVmRefreshFirewallMsg.class.getName());
}
@Autowired
protected ApplianceVmFacade apvmf;
protected abstract List<Flow> getPostCreateFlows();
protected abstract List<Flow> getPostStartFlows();
protected abstract List<Flow> getPostStopFlows();
protected abstract List<Flow> getPostRebootFlows();
protected abstract List<Flow> getPostDestroyFlows();
protected abstract List<Flow> getPostMigrateFlows();
public ApplianceVmBase(VmInstanceVO vo) {
super(vo);
}
protected ApplianceVmVO getSelf() {
return (ApplianceVmVO) self;
}
protected ApplianceVmInventory getInventory() {
return ApplianceVmInventory.valueOf(getSelf());
}
protected List<Flow> createBootstrapFlows(HypervisorType hvType) {
Boolean unitTestOn = CoreGlobalProperty.UNIT_TEST_ON;
List<Flow> flows = new ArrayList<Flow>();
flows.add(apvmf.createBootstrapFlow(hvType));
if (!unitTestOn) {
flows.add(new ApplianceVmConnectFlow());
flows.add(new ApplianceVmDeployAgentFlow());
}
flows.add(new ApplianceVmSetFirewallFlow());
return flows;
}
@Override
protected void destroyHook(VmInstanceDeletionPolicy deletionPolicy, final Completion completion){
logger.debug(String.format("deleting appliance vm[uuid:%s], always use Direct deletion policy", self.getUuid()));
super.doDestroy(VmInstanceDeletionPolicy.Direct, completion);
}
@Override
protected void handleLocalMessage(Message msg) {
if (msg instanceof ApplianceVmRefreshFirewallMsg) {
handle((ApplianceVmRefreshFirewallMsg) msg);
} else if (msg instanceof ApplianceVmAsyncHttpCallMsg) {
handle((ApplianceVmAsyncHttpCallMsg) msg);
} else {
super.handleLocalMessage(msg);
}
}
protected void handle(final ApplianceVmAsyncHttpCallMsg msg) {
thdf.chainSubmit(new ChainTask(msg) {
@Override
public int getSyncLevel() {
return 10;
}
@Override
public String getSyncSignature() {
return String.format("appliancevm-async-httpcall-%s", self.getUuid());
}
@Override
public void run(final SyncTaskChain chain) {
final ApplianceVmAsyncHttpCallReply reply = new ApplianceVmAsyncHttpCallReply();
if (msg.isCheckStatus() && getSelf().getStatus() != ApplianceVmStatus.Connected) {
reply.setError(operr("appliance vm[uuid:%s] is in status of %s that cannot make http call to %s",
self.getUuid(), getSelf().getStatus(), msg.getPath()));
bus.reply(msg, reply);
chain.next();
return;
}
MessageCommandRecorder.record(msg.getCommandClassName());
restf.asyncJsonPost(buildUrl(msg.getPath()), msg.getCommand(), new JsonAsyncRESTCallback<LinkedHashMap>(msg, chain) {
@Override
public void fail(ErrorCode err) {
reply.setError(err);
bus.reply(msg, reply);
chain.next();
}
@Override
public void success(LinkedHashMap ret) {
reply.setResponse(ret);
bus.reply(msg, reply);
chain.next();
}
@Override
public Class<LinkedHashMap> getReturnClass() {
return LinkedHashMap.class;
}
}, TimeUnit.SECONDS, msg.getCommandTimeout());
}
@Override
public String getName() {
return getSyncSignature();
}
});
}
protected void handle(final ApplianceVmRefreshFirewallMsg msg) {
if (msg.isInSyncThread()) {
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return syncThreadName;
}
@Override
public void run(final SyncTaskChain chain) {
refreshFirewall(msg, new NoErrorCompletion(chain) {
@Override
public void done() {
chain.next();
}
});
}
@Override
public String getName() {
return "appliance-vm-refresh-firewall";
}
});
} else {
refreshFirewall(msg, new NoErrorCompletion(msg) {
@Override
public void done() {
// nothing
}
});
}
}
public static String buildAgentUrl(String hostname, String subPath, int port) {
UriComponentsBuilder ub = UriComponentsBuilder.newInstance();
ub.scheme(ApplianceVmGlobalProperty.AGENT_URL_SCHEME);
if (CoreGlobalProperty.UNIT_TEST_ON) {
ub.host("localhost");
} else {
ub.host(hostname);
}
ub.port(port);
if (!"".equals(ApplianceVmGlobalProperty.AGENT_URL_ROOT_PATH)) {
ub.path(ApplianceVmGlobalProperty.AGENT_URL_ROOT_PATH);
}
ub.path(subPath);
return ub.build().toUriString();
}
private String buildUrl(String path) {
String mgmtNicIp = CollectionUtils.find(self.getVmNics(), new Function<String, VmNicVO>() {
@Override
public String call(VmNicVO arg) {
if (arg.getL3NetworkUuid().equals(getSelf().getManagementNetworkUuid())) {
return arg.getIp();
}
return null;
}
});
return buildAgentUrl(mgmtNicIp, path, getSelf().getAgentPort());
}
private void refreshFirewall(final ApplianceVmRefreshFirewallMsg msg, final NoErrorCompletion completion) {
class RuleCombiner {
long total;
Map<String, List<ApplianceVmFirewallRuleVO>> rules = new HashMap<String, List<ApplianceVmFirewallRuleVO>>();
List<ApplianceVmFirewallRuleTO> result = new ArrayList<ApplianceVmFirewallRuleTO>();
Map<String, String> l3NicMacMap = new HashMap<String, String>();
{
for (VmNicVO nic : self.getVmNics()) {
l3NicMacMap.put(nic.getL3NetworkUuid(), nic.getMac());
}
SimpleQuery<ApplianceVmFirewallRuleVO> q = dbf.createQuery(ApplianceVmFirewallRuleVO.class);
q.add(ApplianceVmFirewallRuleVO_.applianceVmUuid, Op.EQ, self.getUuid());
total = q.count();
}
List<ApplianceVmFirewallRuleTO> merge() {
if (total == 0) {
return new ArrayList<ApplianceVmFirewallRuleTO>();
}
prepare();
normalize();
return result;
}
private void normalize() {
for (List<ApplianceVmFirewallRuleVO> vos : rules.values()) {
if (!vos.isEmpty()) {
normalize(vos);
}
}
}
private void normalize(List<ApplianceVmFirewallRuleVO> vos) {
String l3Uuid = null;
String sip = null;
String dip = null;
String allowedCidr = null;
ApplianceVmFirewallProtocol protocol = null;
RangeSet rset = new RangeSet();
for (ApplianceVmFirewallRuleVO vo : vos) {
if (l3Uuid == null) {
l3Uuid = vo.getL3NetworkUuid();
}
if (sip == null) {
sip = vo.getSourceIp();
}
if (dip == null) {
dip = vo.getDestIp();
}
if (allowedCidr == null) {
allowedCidr = vo.getAllowCidr();
}
if (protocol == null) {
protocol = vo.getProtocol();
}
rset.closed(vo.getStartPort(), vo.getEndPort());
}
List<Range> rs = rset.merge();
for (Range r : rs) {
ApplianceVmFirewallRuleTO to = new ApplianceVmFirewallRuleTO();
to.setDestIp(dip);
to.setNicMac(l3NicMacMap.get(l3Uuid));
to.setProtocol(protocol.toString());
to.setAllowCidr(allowedCidr);
to.setSourceIp(sip);
to.setStartPort((int) r.getStart());
to.setEndPort((int) r.getEnd());
result.add(to);
}
}
private void prepare() {
int offset = 0;
int step = 1000;
while (offset < total) {
SimpleQuery<ApplianceVmFirewallRuleVO> q = dbf.createQuery(ApplianceVmFirewallRuleVO.class);
q.add(ApplianceVmFirewallRuleVO_.applianceVmUuid, Op.EQ, self.getUuid());
q.setLimit(step);
q.setStart(offset);
List<ApplianceVmFirewallRuleVO> vos = q.list();
for (ApplianceVmFirewallRuleVO vo : vos) {
String key = String.format("%s-%s-%s-%s-%s",
vo.getL3NetworkUuid(), vo.getProtocol(), vo.getSourceIp(), vo.getDestIp(), vo.getAllowCidr());
List<ApplianceVmFirewallRuleVO> lst = rules.get(key);
if (lst == null) {
lst = new ArrayList<ApplianceVmFirewallRuleVO>();
rules.put(key, lst);
}
lst.add(vo);
}
offset += step;
}
}
}
final ApplianceVmRefreshFirewallReply reply = new ApplianceVmRefreshFirewallReply();
refreshVO();
ErrorCode allowed = validateOperationByState(msg, self.getState(), SysErrors.OPERATION_ERROR);
if (allowed != null) {
reply.setError(allowed);
bus.reply(msg, reply);
completion.done();
return;
}
RefreshFirewallCmd cmd = new RefreshFirewallCmd();
List<ApplianceVmFirewallRuleTO> tos = new RuleCombiner().merge();
cmd.setRules(tos);
restf.asyncJsonPost(buildUrl(ApplianceVmConstant.REFRESH_FIREWALL_PATH), cmd, new JsonAsyncRESTCallback<RefreshFirewallRsp>(msg, completion) {
@Override
public void fail(ErrorCode err) {
reply.setError(err);
bus.reply(msg, reply);
completion.done();
}
@Override
public void success(RefreshFirewallRsp ret) {
if (!ret.isSuccess()) {
logger.warn(String.format("failed to refresh firewall rules on appliance vm[uuid:%s, name:%s], %s",
self.getUuid(), self.getName(), ret.getError()));
reply.setError(operr(ret.getError()));
}
bus.reply(msg, reply);
completion.done();
}
@Override
public Class<RefreshFirewallRsp> getReturnClass() {
return RefreshFirewallRsp.class;
}
});
}
@Override
protected VmInstanceVO refreshVO() {
self = dbf.findByUuid(self.getUuid(), ApplianceVmVO.class);
return self;
}
@Override
protected VmInstanceSpec buildSpecFromInventory(VmInstanceInventory inv, VmOperation operation) {
VmInstanceSpec spec = super.buildSpecFromInventory(inv, operation);
spec.putExtensionData(ApplianceVmConstant.Params.applianceVmSubType.toString(), getSelf().getApplianceVmType());
return spec;
}
private void prepareLifeCycleInfo(FlowChain chain) {
ApplianceVmPostLifeCycleInfo info = new ApplianceVmPostLifeCycleInfo();
ApplianceVmInventory ainv = ApplianceVmInventory.valueOf(getSelf());
L3NetworkVO defaultRouteL3VO = dbf.findByUuid(ainv.getDefaultRouteL3NetworkUuid(), L3NetworkVO.class);
info.setDefaultRouteL3Network(L3NetworkInventory.valueOf(defaultRouteL3VO));
info.setManagementNic(ainv.getManagementNic());
chain.getData().put(ApplianceVmConstant.Params.applianceVmInfoForPostLifeCycle.toString(), info);
}
private void prepareFirewallInfo(FlowChain chain) {
SimpleQuery<ApplianceVmFirewallRuleVO> q = dbf.createQuery(ApplianceVmFirewallRuleVO.class);
q.add(ApplianceVmFirewallRuleVO_.applianceVmUuid, Op.EQ, getSelf().getUuid());
List<ApplianceVmFirewallRuleVO> vos = q.list();
List<ApplianceVmFirewallRuleInventory> rules = ApplianceVmFirewallRuleInventory.valueOf(vos);
chain.getData().put(ApplianceVmConstant.Params.applianceVmFirewallRules.toString(), rules);
}
@Override
protected FlowChain getStopVmWorkFlowChain(VmInstanceInventory inv) {
FlowChain chain = super.getStopVmWorkFlowChain(inv);
chain.setName(String.format("stop-appliancevm-%s", inv.getUuid()));
chain.insert(new Flow() {
String __name__ = "change-appliancevm-status-to-disconnected";
ApplianceVmStatus originStatus = getSelf().getStatus();
@Override
public void run(FlowTrigger trigger, Map data) {
getSelf().setStatus(ApplianceVmStatus.Disconnected);
self = dbf.updateAndRefresh(self);
trigger.next();
}
@Override
public void rollback(FlowRollback trigger, Map data) {
self = dbf.reload(self);
getSelf().setStatus(originStatus);
self = dbf.updateAndRefresh(self);
trigger.rollback();
}
});
prepareLifeCycleInfo(chain);
prepareFirewallInfo(chain);
List<Flow> subStopFlows = getPostStopFlows();
if (subStopFlows != null) {
for (Flow f : subStopFlows) {
chain.then(f);
}
}
boolean noRollbackOnFailure = ApplianceVmGlobalProperty.NO_ROLLBACK_ON_POST_FAILURE;
chain.noRollback(noRollbackOnFailure);
return chain;
}
@Override
protected FlowChain getDestroyVmWorkFlowChain(VmInstanceInventory inv) {
FlowChain chain = super.getDestroyVmWorkFlowChain(inv);
chain.setName(String.format("destroy-appliancevm-%s", inv.getUuid()));
chain.insert(new Flow() {
String __name__ = "change-appliancevm-status-to-disconnected";
ApplianceVmStatus originStatus = getSelf().getStatus();
public void run(FlowTrigger trigger, Map data) {
getSelf().setStatus(ApplianceVmStatus.Disconnected);
self = dbf.updateAndRefresh(self);
trigger.next();
}
@Override
public void rollback(FlowRollback trigger, Map data) {
self = dbf.reload(self);
getSelf().setStatus(originStatus);
self = dbf.updateAndRefresh(self);
trigger.rollback();
}
});
prepareLifeCycleInfo(chain);
prepareFirewallInfo(chain);
List<Flow> subDestroyFlows = getPostDestroyFlows();
if (subDestroyFlows != null) {
for (Flow f : subDestroyFlows) {
chain.then(f);
}
}
boolean noRollbackOnFailure = ApplianceVmGlobalProperty.NO_ROLLBACK_ON_POST_FAILURE;
chain.noRollback(noRollbackOnFailure);
return chain;
}
@Override
protected FlowChain getMigrateVmWorkFlowChain(VmInstanceInventory inv) {
FlowChain chain = super.getMigrateVmWorkFlowChain(inv);
chain.setName(String.format("migrate-appliancevm-%s", inv.getUuid()));
prepareLifeCycleInfo(chain);
prepareFirewallInfo(chain);
List<Flow> subMigrateFlows = getPostMigrateFlows();
if (subMigrateFlows != null) {
for (Flow f : subMigrateFlows) {
chain.then(f);
}
}
boolean noRollbackOnFailure = ApplianceVmGlobalProperty.NO_ROLLBACK_ON_POST_FAILURE;
chain.noRollback(noRollbackOnFailure);
return chain;
}
@Override
protected FlowChain getRebootVmWorkFlowChain(VmInstanceInventory inv) {
FlowChain chain = super.getRebootVmWorkFlowChain(inv);
chain.setName(String.format("reboot-appliancevm-%s", inv.getUuid()));
chain.insert(new Flow() {
String __name__ = "change-appliancevm-status-to-disconnected";
ApplianceVmStatus originStatus;
@Override
public void run(FlowTrigger trigger, Map data) {
originStatus = getSelf().getStatus();
getSelf().setStatus(ApplianceVmStatus.Disconnected);
self = dbf.updateAndRefresh(self);
trigger.next();
}
@Override
public void rollback(FlowRollback trigger, Map data) {
self = dbf.reload(self);
getSelf().setStatus(originStatus);
self = dbf.updateAndRefresh(self);
trigger.rollback();
}
});
prepareLifeCycleInfo(chain);
prepareFirewallInfo(chain);
addBootstrapFlows(chain, HypervisorType.valueOf(inv.getHypervisorType()));
List<Flow> subRebootFlows = getPostRebootFlows();
if (subRebootFlows != null) {
for (Flow f : subRebootFlows) {
chain.then(f);
}
}
chain.then(new NoRollbackFlow() {
String __name__ = "change-appliancevm-status-to-connected";
@Override
public void run(FlowTrigger trigger, Map data) {
getSelf().setStatus(ApplianceVmStatus.Connected);
self = dbf.updateAndRefresh(self);
trigger.next();
}
});
boolean noRollbackOnFailure = ApplianceVmGlobalProperty.NO_ROLLBACK_ON_POST_FAILURE;
chain.noRollback(noRollbackOnFailure);
return chain;
}
@Override
protected FlowChain getStartVmWorkFlowChain(VmInstanceInventory inv) {
FlowChain chain = super.getStartVmWorkFlowChain(inv);
chain.setName(String.format("start-appliancevm-%s", inv.getUuid()));
chain.insert(new Flow() {
String __name__ = "change-appliancevm-status-to-connecting";
ApplianceVmStatus originStatus = getSelf().getStatus();
@Override
public void run(FlowTrigger trigger, Map data) {
getSelf().setStatus(ApplianceVmStatus.Connecting);
self = dbf.updateAndRefresh(self);
trigger.next();
}
@Override
public void rollback(FlowRollback trigger, Map data) {
self = dbf.reload(self);
getSelf().setStatus(originStatus);
self = dbf.updateAndRefresh(self);
trigger.rollback();
}
});
prepareLifeCycleInfo(chain);
prepareFirewallInfo(chain);
addBootstrapFlows(chain, HypervisorType.valueOf(inv.getHypervisorType()));
List<Flow> subStartFlows = getPostStartFlows();
if (subStartFlows != null) {
for (Flow f : subStartFlows) {
chain.then(f);
}
}
chain.then(new NoRollbackFlow() {
String __name__ = "change-appliancevm-status-to-connected";
@Override
public void run(FlowTrigger trigger, Map data) {
getSelf().setStatus(ApplianceVmStatus.Connected);
self = dbf.updateAndRefresh(self);
trigger.next();
}
});
boolean noRollbackOnFailure = ApplianceVmGlobalProperty.NO_ROLLBACK_ON_POST_FAILURE;
chain.noRollback(noRollbackOnFailure);
return chain;
}
private FlowChain addBootstrapFlows(FlowChain chain, HypervisorType hvType) {
for (Flow flow : createBootstrapFlows(hvType)) {
chain.then(flow);
}
return chain;
}
@Override
protected void startVmFromNewCreate(final StartNewCreatedVmInstanceMsg msg, final SyncTaskChain taskChain) {
boolean callNext = true;
try {
refreshVO();
ErrorCode allowed = validateOperationByState(msg, self.getState(), null);
if (allowed != null) {
bus.replyErrorByMessageType(msg, allowed);
return;
}
ErrorCode preCreated = extEmitter.preStartNewCreatedVm(msg.getVmInstanceInventory());
if (preCreated != null) {
bus.replyErrorByMessageType(msg, preCreated);
return;
}
StartNewCreatedApplianceVmMsg smsg = (StartNewCreatedApplianceVmMsg) msg;
ApplianceVmSpec aspec = smsg.getApplianceVmSpec();
final VmInstanceSpec spec = new VmInstanceSpec();
spec.setVmInventory(msg.getVmInstanceInventory());
if (msg.getL3NetworkUuids() != null && !msg.getL3NetworkUuids().isEmpty()) {
SimpleQuery<L3NetworkVO> nwquery = dbf.createQuery(L3NetworkVO.class);
nwquery.add(L3NetworkVO_.uuid, SimpleQuery.Op.IN, msg.getL3NetworkUuids());
List<L3NetworkVO> vos = nwquery.list();
List<L3NetworkInventory> nws = L3NetworkInventory.valueOf(vos);
spec.setL3Networks(nws);
} else {
spec.setL3Networks(new ArrayList<L3NetworkInventory>(0));
}
if (msg.getDataDiskOfferingUuids() != null && !msg.getDataDiskOfferingUuids().isEmpty()) {
SimpleQuery<DiskOfferingVO> dquery = dbf.createQuery(DiskOfferingVO.class);
dquery.add(DiskOfferingVO_.uuid, SimpleQuery.Op.IN, msg.getDataDiskOfferingUuids());
List<DiskOfferingVO> vos = dquery.list();
// allow create multiple data volume from the same disk offering
List<DiskOfferingInventory> disks = new ArrayList<>();
for (final String duuid : msg.getDataDiskOfferingUuids()) {
DiskOfferingVO dvo = CollectionUtils.find(vos, new Function<DiskOfferingVO, DiskOfferingVO>() {
@Override
public DiskOfferingVO call(DiskOfferingVO arg) {
if (duuid.equals(arg.getUuid())) {
return arg;
}
return null;
}
});
disks.add(DiskOfferingInventory.valueOf(dvo));
}
spec.setDataDiskOfferings(disks);
} else {
spec.setDataDiskOfferings(new ArrayList<>(0));
}
ImageVO imvo = dbf.findByUuid(spec.getVmInventory().getImageUuid(), ImageVO.class);
spec.getImageSpec().setInventory(ImageInventory.valueOf(imvo));
spec.putExtensionData(ApplianceVmConstant.Params.applianceVmSpec.toString(), aspec);
spec.setCurrentVmOperation(VmInstanceConstant.VmOperation.NewCreate);
spec.putExtensionData(ApplianceVmConstant.Params.applianceVmSubType.toString(), getSelf().getApplianceVmType());
spec.setBootOrders(list(VmBootDevice.HardDisk.toString()));
changeVmStateInDb(VmInstanceStateEvent.starting);
extEmitter.beforeStartNewCreatedVm(VmInstanceInventory.valueOf(self));
FlowChain chain = apvmf.getCreateApplianceVmWorkFlowBuilder().build();
setFlowMarshaller(chain);
chain.setName(String.format("create-appliancevm-%s", msg.getVmInstanceUuid()));
chain.getData().put(VmInstanceConstant.Params.VmInstanceSpec.toString(), spec);
chain.getData().put(ApplianceVmConstant.Params.applianceVmFirewallRules.toString(), aspec.getFirewallRules());
addBootstrapFlows(chain, VolumeFormat.getMasterHypervisorTypeByVolumeFormat(imvo.getFormat()));
List<Flow> subCreateFlows = getPostCreateFlows();
if (subCreateFlows != null) {
for (Flow f : subCreateFlows) {
chain.then(f);
}
}
chain.then(new NoRollbackFlow() {
String __name__ = "change-appliancevm-status-to-connected";
@Override
public void run(FlowTrigger trigger, Map data) {
// must reload here, otherwise it will override changes created by previous flows
self = dbf.reload(self);
getSelf().setStatus(ApplianceVmStatus.Connected);
dbf.update(self);
trigger.next();
}
});
boolean noRollbackOnFailure = ApplianceVmGlobalProperty.NO_ROLLBACK_ON_POST_FAILURE;
chain.noRollback(noRollbackOnFailure);
chain.done(new FlowDoneHandler(msg, taskChain) {
@Override
public void handle(Map data) {
VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
self = dbf.reload(self);
self.setLastHostUuid(spec.getDestHost().getUuid());
self.setHostUuid(spec.getDestHost().getUuid());
self.setClusterUuid(spec.getDestHost().getClusterUuid());
self.setZoneUuid(spec.getDestHost().getZoneUuid());
self.setHypervisorType(spec.getDestHost().getHypervisorType());
self.setRootVolumeUuid(spec.getDestRootVolume().getUuid());
changeVmStateInDb(VmInstanceStateEvent.running);
logger.debug(String.format("appliance vm[uuid:%s, name: %s, type:%s] is running ..",
self.getUuid(), self.getName(), getSelf().getApplianceVmType()));
VmInstanceInventory inv = VmInstanceInventory.valueOf(self);
extEmitter.afterStartNewCreatedVm(inv);
StartNewCreatedVmInstanceReply reply = new StartNewCreatedVmInstanceReply();
reply.setVmInventory(inv);
bus.reply(msg, reply);
taskChain.next();
}
}).error(new FlowErrorHandler(msg, taskChain) {
@Override
public void handle(ErrorCode errCode, Map data) {
extEmitter.failedToStartNewCreatedVm(VmInstanceInventory.valueOf(self), errCode);
dbf.remove(self);
StartNewCreatedVmInstanceReply reply = new StartNewCreatedVmInstanceReply();
reply.setError(errCode);
reply.setSuccess(false);
bus.reply(msg, reply);
taskChain.next();
}
}).start();
callNext = false;
} finally {
if (callNext) {
taskChain.next();
}
}
}
@Override
protected void selectDefaultL3(VmNicInventory nic) {
return;
}
}