package org.zstack.network.service.virtualrouter.dhcp;
import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.core.timeout.ApiTimeoutManager;
import org.zstack.header.core.Completion;
import org.zstack.header.core.NoErrorCompletion;
import org.zstack.header.core.ReturnValueCompletion;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.message.MessageReply;
import org.zstack.header.network.service.DhcpStruct;
import org.zstack.header.network.service.NetworkServiceDhcpBackend;
import org.zstack.header.network.service.NetworkServiceProviderType;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.header.vm.VmInstanceInventory;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.vm.VmNicInventory;
import org.zstack.network.service.virtualrouter.*;
import org.zstack.network.service.virtualrouter.VirtualRouterCommands.AddDhcpEntryRsp;
import org.zstack.utils.CollectionDSL;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.function.Function;
import org.zstack.utils.logging.CLogger;
import static org.zstack.core.Platform.operr;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class VirtualRouterDhcpBackend extends AbstractVirtualRouterBackend implements NetworkServiceDhcpBackend {
private final CLogger logger = Utils.getLogger(VirtualRouterDhcpBackend.class);
@Autowired
protected ErrorFacade errf;
@Autowired
protected CloudBus bus;
@Autowired
protected ApiTimeoutManager apiTimeoutManager;
@Override
public NetworkServiceProviderType getProviderType() {
return VirtualRouterConstant.PROVIDER_TYPE;
}
private void applyDhcpEntry(final Iterator<DhcpStruct> it, final VmInstanceSpec spec, final Completion completion) {
if (!it.hasNext()) {
completion.success();
return;
}
final DhcpStruct struct = it.next();
VirtualRouterStruct s = new VirtualRouterStruct();
s.setL3Network(struct.getL3Network());
acquireVirtualRouterVm(s, new ReturnValueCompletion<VirtualRouterVmInventory>(completion) {
@Override
public void success(final VirtualRouterVmInventory vr) {
VirtualRouterCommands.DhcpInfo e = new VirtualRouterCommands.DhcpInfo();
e.setGateway(struct.getGateway());
e.setIp(struct.getIp());
e.setDefaultL3Network(struct.isDefaultL3Network());
e.setMac(struct.getMac());
e.setGateway(struct.getGateway());
e.setNetmask(struct.getNetmask());
e.setDnsDomain(struct.getDnsDomain());
e.setHostname(struct.getHostname());
if (e.isDefaultL3Network()) {
if (e.getHostname() == null) {
e.setHostname(e.getIp().replaceAll("\\.", "-"));
}
if (e.getDnsDomain() != null) {
e.setHostname(String.format("%s.%s", e.getHostname(), e.getDnsDomain()));
}
}
VmNicInventory vrNic = CollectionUtils.find(vr.getVmNics(), new Function<VmNicInventory, VmNicInventory>() {
@Override
public VmNicInventory call(VmNicInventory arg) {
return arg.getL3NetworkUuid().equals(struct.getL3Network().getUuid()) ? arg : null;
}
});
e.setVrNicMac(vrNic.getMac());
if (struct.isDefaultL3Network()) {
e.setDns(CollectionDSL.list(vrNic.getIp()));
}
VirtualRouterCommands.AddDhcpEntryCmd cmd = new VirtualRouterCommands.AddDhcpEntryCmd();
cmd.setDhcpEntries(Arrays.asList(e));
VirtualRouterAsyncHttpCallMsg cmsg = new VirtualRouterAsyncHttpCallMsg();
cmsg.setCommand(cmd);
cmsg.setCommandTimeout(apiTimeoutManager.getTimeout(cmd.getClass(), "30m"));
cmsg.setPath(VirtualRouterConstant.VR_ADD_DHCP_PATH);
cmsg.setVmInstanceUuid(vr.getUuid());
cmsg.setCheckStatus(true);
bus.makeTargetServiceIdByResourceUuid(cmsg, VmInstanceConstant.SERVICE_ID, vr.getUuid());
bus.send(cmsg, new CloudBusCallBack(completion) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
completion.fail(reply.getError());
return;
}
VirtualRouterAsyncHttpCallReply re = reply.castReply();
AddDhcpEntryRsp rsp = re.toResponse(AddDhcpEntryRsp.class);
if (rsp.isSuccess()) {
new VirtualRouterRoleManager().makeDhcpRole(vr.getUuid());
logger.debug(String.format("successfully add dhcp entry[%s] to virtual router vm[uuid:%s, ip:%s]", struct, vr.getUuid(), vr.getManagementNic()
.getIp()));
applyDhcpEntry(it, spec, completion);
} else {
ErrorCode err = operr("unable to add dhcp entries to virtual router vm[uuid:%s ip:%s], because %s, dhcp entry[%s]",
vr.getUuid(), vr.getManagementNic().getIp(), rsp.getError(), struct);
completion.fail(err);
}
}
});
}
@Override
public void fail(ErrorCode errorCode) {
completion.fail(errorCode);
}
});
}
@Override
public void applyDhcpService(final List<DhcpStruct> dhcpStructList, final VmInstanceSpec spec, final Completion completion) {
if (dhcpStructList.isEmpty()) {
completion.success();
return;
}
applyDhcpEntry(dhcpStructList.iterator(), spec, completion);
}
private void releaseDhcp(final Iterator<DhcpStruct> it, final VmInstanceSpec spec, final NoErrorCompletion completion) {
if (!it.hasNext()) {
completion.done();
return;
}
final DhcpStruct struct = it.next();
if (!vrMgr.isVirtualRouterRunningForL3Network(struct.getL3Network().getUuid())) {
logger.debug(String.format("virtual router for l3Network[uuid:%s] is not running, skip releasing DHCP", struct.getL3Network().getUuid()));
releaseDhcp(it, spec, completion);
return;
}
final VirtualRouterVmInventory vr = vrMgr.getVirtualRouterVm(struct.getL3Network());
VmNicInventory vrNic = CollectionUtils.find(vr.getVmNics(), new Function<VmNicInventory, VmNicInventory>() {
@Override
public VmNicInventory call(VmNicInventory arg) {
return arg.getL3NetworkUuid().equals(struct.getL3Network().getUuid()) ? arg : null;
}
});
VirtualRouterCommands.DhcpInfo e = new VirtualRouterCommands.DhcpInfo();
e.setGateway(struct.getGateway());
e.setDefaultL3Network(struct.isDefaultL3Network());
e.setIp(struct.getIp());
e.setMac(struct.getMac());
e.setNetmask(struct.getNetmask());
e.setVrNicMac(vrNic.getMac());
VirtualRouterCommands.RemoveDhcpEntryCmd cmd = new VirtualRouterCommands.RemoveDhcpEntryCmd();
cmd.setDhcpEntries(Arrays.asList(e));
VirtualRouterAsyncHttpCallMsg msg = new VirtualRouterAsyncHttpCallMsg();
msg.setCheckStatus(true);
msg.setVmInstanceUuid(vr.getUuid());
msg.setPath(VirtualRouterConstant.VR_REMOVE_DHCP_PATH);
msg.setCommand(cmd);
msg.setCommandTimeout(apiTimeoutManager.getTimeout(cmd.getClass(), "30m"));
bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, vr.getUuid());
bus.send(msg, new CloudBusCallBack(completion) {
@Override
public void run(MessageReply reply) {
if (!reply.isSuccess()) {
logger.warn(String.format("unable to remove dhcp entry[%s] from virtual router vm[uuid:%s, ip:%s], %s", struct, vr.getUuid(), vr
.getManagementNic().getIp(), reply.getError()));
//TODO: GC
} else {
VirtualRouterAsyncHttpCallReply ret = reply.castReply();
if (ret.isSuccess()) {
logger.debug(String.format("successfully removed dhcp entry[%s] from virtual router vm[uuid:%s, ip:%s]", struct, vr.getUuid(), vr
.getManagementNic().getIp()));
} else {
logger.warn(String.format("unable to remove dhcp entry[%s] from virtual router vm[uuid:%s, ip:%s], %s", struct, vr.getUuid(), vr
.getManagementNic().getIp(), ret.getError()));
//TODO: GC
}
}
releaseDhcp(it, spec, completion);
}
});
}
@Override
public void releaseDhcpService(List<DhcpStruct> dhcpStructList, VmInstanceSpec spec, NoErrorCompletion completion) {
if (dhcpStructList.isEmpty()) {
completion.done();
return;
}
releaseDhcp(dhcpStructList.iterator(), spec, completion);
}
@Override
public void vmDefaultL3NetworkChanged(VmInstanceInventory vm, String previousL3, String nowL3, Completion completion) {
return;
}
}