package org.zstack.appliancevm;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.zstack.appliancevm.ApplianceVmCommands.InitCmd;
import org.zstack.appliancevm.ApplianceVmCommands.InitRsp;
import org.zstack.appliancevm.ApplianceVmConstant.Params;
import org.zstack.core.CoreGlobalProperty;
import org.zstack.core.ansible.AnsibleFacade;
import org.zstack.core.ansible.AnsibleGlobalProperty;
import org.zstack.core.ansible.AnsibleRunner;
import org.zstack.core.ansible.SshFileMd5Checker;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.core.timeout.ApiTimeoutManager;
import org.zstack.core.workflow.FlowChainBuilder;
import org.zstack.core.workflow.ShareFlow;
import org.zstack.header.core.Completion;
import org.zstack.header.core.workflow.*;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.message.MessageReply;
import org.zstack.header.rest.RESTFacade;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.header.vm.VmInstanceConstant.VmOperation;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.vm.VmNicInventory;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.function.Function;
import org.zstack.utils.path.PathUtil;
import static org.zstack.core.Platform.operr;
import java.util.Map;
/**
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class ApplianceVmDeployAgentFlow extends NoRollbackFlow {
@Autowired
private DatabaseFacade dbf;
@Autowired
private RESTFacade restf;
@Autowired
private CloudBus bus;
@Autowired
private ErrorFacade errf;
@Autowired
private ApiTimeoutManager apiTimeoutManager;
@Autowired
private AnsibleFacade asf;
private void continueConnect(final String echoUrl, final String apvmUuid, final FlowTrigger outerTrigger) {
FlowChain chain = FlowChainBuilder.newShareFlowChain();
chain.setName("continue-connect-appliance-vm");
chain.then(new ShareFlow() {
@Override
public void setup() {
flow(new NoRollbackFlow() {
String __name__ = "echo";
@Override
public void run(final FlowTrigger trigger, Map data) {
restf.echo(echoUrl, new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
});
flow(new NoRollbackFlow() {
String __name__ = "init";
@Override
public void run(final FlowTrigger trigger, Map data) {
InitCmd cmd = new InitCmd();
ApplianceVmAsyncHttpCallMsg msg = new ApplianceVmAsyncHttpCallMsg();
msg.setVmInstanceUuid(apvmUuid);
msg.setCommand(cmd);
msg.setCommandTimeout(apiTimeoutManager.getTimeout(cmd.getClass(), "5m"));
msg.setCheckStatus(false);
msg.setPath(ApplianceVmConstant.INIT_PATH);
bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, apvmUuid);
bus.send(msg, new CloudBusCallBack(trigger) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
trigger.fail(reply.getError());
return;
}
ApplianceVmAsyncHttpCallReply ar = reply.castReply();
InitRsp rsp = ar.toResponse(InitRsp.class);
if (!rsp.isSuccess()) {
trigger.fail(operr(rsp.getError()));
return;
}
trigger.next();
}
});
}
});
done(new FlowDoneHandler(outerTrigger) {
@Override
public void handle(Map data) {
outerTrigger.next();
}
});
error(new FlowErrorHandler(outerTrigger) {
@Override
public void handle(ErrorCode errCode, Map data) {
outerTrigger.fail(errCode);
}
});
}
}).start();
}
@Override
public void run(final FlowTrigger trigger, Map data) {
boolean isReconnect = Boolean.valueOf((String) data.get(Params.isReconnect.toString()));
final String apvmUuid;
String mgmtNicIp;
if (!isReconnect) {
VmNicInventory mgmtNic;
final VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
if (spec.getCurrentVmOperation() == VmOperation.NewCreate) {
final ApplianceVmSpec aspec = spec.getExtensionData(ApplianceVmConstant.Params.applianceVmSpec.toString(), ApplianceVmSpec.class);
mgmtNic = CollectionUtils.find(spec.getDestNics(), new Function<VmNicInventory, VmNicInventory>() {
@Override
public VmNicInventory call(VmNicInventory arg) {
return arg.getL3NetworkUuid().equals(aspec.getManagementNic().getL3NetworkUuid()) ? arg : null;
}
});
apvmUuid = spec.getVmInventory().getUuid();
} else {
ApplianceVmVO avo = dbf.findByUuid(spec.getVmInventory().getUuid(), ApplianceVmVO.class);
ApplianceVmInventory ainv = ApplianceVmInventory.valueOf(avo);
mgmtNic = ainv.getManagementNic();
apvmUuid = avo.getUuid();
}
mgmtNicIp = mgmtNic.getIp();
} else {
mgmtNicIp = (String) data.get(Params.managementNicIp.toString());
apvmUuid = (String) data.get(Params.applianceVmUuid.toString());
}
final String mgmtIp = mgmtNicIp;
final String url = ApplianceVmBase.buildAgentUrl(mgmtIp, ApplianceVmConstant.ECHO_PATH, 7759);
if (CoreGlobalProperty.UNIT_TEST_ON) {
continueConnect(url, apvmUuid, trigger);
return;
} else if (!isReconnect && !ApplianceVmGlobalConfig.DEPLOY_AGENT_ON_START.value(Boolean.class)) {
continueConnect(url, apvmUuid, trigger);
return;
}
final String username = "root";
final String privKey = asf.getPrivateKey();
SshFileMd5Checker checker = new SshFileMd5Checker();
checker.setTargetIp(mgmtIp);
checker.setUsername(username);
checker.setPrivateKey(privKey);
checker.addSrcDestPair(SshFileMd5Checker.ZSTACKLIB_SRC_PATH, String.format("/var/lib/zstack/appliancevm/package/%s", AnsibleGlobalProperty.ZSTACKLIB_PACKAGE_NAME));
checker.addSrcDestPair(PathUtil.findFileOnClassPath(String.format("ansible/appliancevm/%s", ApplianceVmGlobalProperty.AGENT_PACKAGE_NAME), true).getAbsolutePath(),
String.format("/var/lib/zstack/appliancevm/package/%s", ApplianceVmGlobalProperty.AGENT_PACKAGE_NAME));
AnsibleRunner runner = new AnsibleRunner();
runner.installChecker(checker);
runner.setUsername(username);
runner.setPlayBookName(ApplianceVmConstant.ANSIBLE_PLAYBOOK_NAME);
runner.setPrivateKey(privKey);
runner.setAgentPort(ApplianceVmGlobalProperty.AGENT_PORT);
runner.setTargetIp(mgmtIp);
runner.putArgument("pkg_appliancevm", ApplianceVmGlobalProperty.AGENT_PACKAGE_NAME);
runner.run(new Completion(trigger) {
@Override
public void success() {
continueConnect(url, apvmUuid, trigger);
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
}