package org.zstack.network.service.virtualrouter.vyos;
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.*;
import org.zstack.appliancevm.ApplianceVmConstant.Params;
import org.zstack.core.CoreGlobalProperty;
import org.zstack.core.ansible.AnsibleFacade;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.core.thread.CancelablePeriodicTask;
import org.zstack.core.thread.ThreadFacade;
import org.zstack.header.core.workflow.FlowTrigger;
import org.zstack.header.core.workflow.NoRollbackFlow;
import org.zstack.header.errorcode.OperationFailureException;
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.network.NetworkUtils;
import org.zstack.utils.path.PathUtil;
import org.zstack.utils.ssh.Ssh;
import static org.zstack.core.Platform.operr;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Created by xing5 on 2016/10/31.
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class VyosDeployAgentFlow extends NoRollbackFlow {
@Autowired
private AnsibleFacade asf;
@Autowired
private DatabaseFacade dbf;
@Autowired
private ThreadFacade thdf;
@Autowired
private ErrorFacade errf;
@Override
public void run(FlowTrigger trigger, Map data) {
if (CoreGlobalProperty.UNIT_TEST_ON) {
trigger.next();
return;
}
boolean isReconnect = Boolean.valueOf((String) data.get(Params.isReconnect.toString()));
if (!isReconnect && !ApplianceVmGlobalConfig.DEPLOY_AGENT_ON_START.value(Boolean.class)) {
// no need to deploy agent
trigger.next();
return;
}
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;
}
});
} else {
ApplianceVmVO avo = dbf.findByUuid(spec.getVmInventory().getUuid(), ApplianceVmVO.class);
ApplianceVmInventory ainv = ApplianceVmInventory.valueOf(avo);
mgmtNic = ainv.getManagementNic();
}
mgmtNicIp = mgmtNic.getIp();
} else {
mgmtNicIp = (String) data.get(Params.managementNicIp.toString());
}
int timeoutInSeconds = ApplianceVmGlobalConfig.CONNECT_TIMEOUT.value(Integer.class);
long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(timeoutInSeconds);
if (isReconnect && !NetworkUtils.isRemotePortOpen(mgmtNicIp, 22, 2000)) {
throw new OperationFailureException(operr("unable to ssh in to the vyos[%s], the ssh port seems not open", mgmtNicIp));
}
thdf.submitCancelablePeriodicTask(new CancelablePeriodicTask() {
@Override
public boolean run() {
long now = System.currentTimeMillis();
if (now > timeout) {
trigger.fail(errf.instantiateErrorCode(ApplianceVmErrors.UNABLE_TO_START, String.format("the SSH port is not" +
" open after %s seconds. Failed to login the vyos router[ip:%s]", timeoutInSeconds, mgmtNicIp)));
return true;
}
if (NetworkUtils.isRemotePortOpen(mgmtNicIp, 22, 2)) {
deployAgent();
return true;
} else {
return false;
}
}
private void deployAgent() {
new Ssh().scp(
PathUtil.findFileOnClassPath("ansible/zvr/zvr.bin", true).getAbsolutePath(),
"/home/vyos/zvr.bin"
).scp(
PathUtil.findFileOnClassPath("ansible/zvr/zvrboot.bin", true).getAbsolutePath(),
"/home/vyos/zvrboot.bin"
).setPrivateKey(asf.getPrivateKey()).setUsername("vyos").setHostname(mgmtNicIp).setPort(22).runErrorByExceptionAndClose();
new Ssh().shell("sudo bash /home/vyos/zvrboot.bin\n" +
"sudo bash /home/vyos/zvr.bin\n" +
"sudo bash /etc/init.d/zstack-virtualrouteragent restart\n"
).setPrivateKey(asf.getPrivateKey()).setUsername("vyos").setHostname(mgmtNicIp).setPort(22).runErrorByExceptionAndClose();
trigger.next();
/*
final String username = "vyos";
final String privKey = asf.getPrivateKey();
SshFileMd5Checker checker = new SshFileMd5Checker();
checker.setTargetIp(mgmtNicIp);
checker.setUsername(username);
checker.setPrivateKey(privKey);
checker.addSrcDestPair(PathUtil.findFileOnClassPath("ansible/zvr/zvr.bin", true).getAbsolutePath(),
"/home/vyos/zvr.bin");
checker.addSrcDestPair(PathUtil.findFileOnClassPath("ansible/zvr/zvrboot.bin", true).getAbsolutePath(),
"/home/vyos/zvrboot.bin");
AnsibleRunner runner = new AnsibleRunner();
runner.installChecker(checker);
runner.putArgument("remote_root", "/home/vyos/zvr");
runner.setUsername(username);
runner.setPlayBookName(VyosConstants.ANSIBLE_PLAYBOOK_NAME);
runner.setPrivateKey(privKey);
runner.setAgentPort(ApplianceVmGlobalProperty.AGENT_PORT);
runner.setTargetIp(mgmtNicIp);
runner.run(new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
*/
}
@Override
public TimeUnit getTimeUnit() {
return TimeUnit.SECONDS;
}
@Override
public long getInterval() {
return 1;
}
@Override
public String getName() {
return VyosDeployAgentFlow.class.getName();
}
});
}
}