package org.zstack.network.service.flat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.zstack.core.Platform; import org.zstack.core.cloudbus.CloudBus; import org.zstack.core.cloudbus.CloudBusCallBack; import org.zstack.core.cloudbus.CloudBusListCallBack; import org.zstack.core.db.DatabaseFacade; 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.workflow.Flow; import org.zstack.header.core.workflow.FlowTrigger; import org.zstack.header.core.workflow.NoRollbackFlow; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.host.*; import org.zstack.header.message.MessageReply; import org.zstack.header.network.l3.L3NetworkInventory; import org.zstack.header.network.service.DnsStruct; import org.zstack.header.network.service.NetworkServiceDnsBackend; import org.zstack.header.network.service.NetworkServiceProviderType; import org.zstack.header.vm.VmInstanceSpec; import org.zstack.kvm.*; import org.zstack.utils.CollectionUtils; import org.zstack.utils.Utils; import org.zstack.utils.function.Function; import org.zstack.utils.logging.CLogger; import javax.persistence.TypedQuery; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; /** * Created by frank on 9/15/2015. */ public class FlatDnsBackend implements NetworkServiceDnsBackend, KVMHostConnectExtensionPoint, HostConnectionReestablishExtensionPoint { private static final CLogger logger = Utils.getLogger(FlatDnsBackend.class); @Autowired private CloudBus bus; @Autowired private ErrorFacade errf; @Autowired private DatabaseFacade dbf; @Autowired private ApiTimeoutManager timeoutMgr; public static final String SET_DNS_PATH = "/flatnetworkprovider/dns/set"; @Transactional(readOnly = true) private List<String> getDnsByClusterUuid(String cuuid) { String sql = "select dns.dns from L3NetworkDnsVO dns, L3NetworkVO l3, L2NetworkClusterRefVO l2ref where" + " dns.l3NetworkUuid = l3.uuid and l3.l2NetworkUuid = l2ref.l2NetworkUuid and l2ref.clusterUuid = :cuuid"; TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class); q.setParameter("cuuid", cuuid); return q.getResultList(); } private void setDnsOnHost(HostInventory host, final Completion completion) { List<String> dns = getDnsByClusterUuid(host.getClusterUuid()); if (dns.isEmpty()) { completion.success(); return; } SetDnsCmd cmd = new SetDnsCmd(); cmd.dns = dns; KVMHostAsyncHttpCallMsg msg = new KVMHostAsyncHttpCallMsg(); msg.setHostUuid(host.getUuid()); msg.setCommand(cmd); msg.setCommandTimeout(timeoutMgr.getTimeout(cmd.getClass(), "5m")); msg.setPath(SET_DNS_PATH); bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, host.getUuid()); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (!reply.isSuccess()) { completion.fail(reply.getError()); return; } KVMHostAsyncHttpCallReply re = (KVMHostAsyncHttpCallReply)reply; SetDnsRsp rsp = re.castReply(); if (!rsp.isSuccess()) { completion.fail(Platform.operr(rsp.getError())); } } }); } @Override public void connectionReestablished(HostInventory inv) throws HostException { setDnsOnHost(inv, new Completion(null) { @Override public void success() { // ignore } @Override public void fail(ErrorCode errorCode) { logger.warn(errorCode.toString()); } }); } @Override public HypervisorType getHypervisorTypeForReestablishExtensionPoint() { return KVMHostFactory.hypervisorType; } @Override public Flow createKvmHostConnectingFlow(final KVMHostConnectedContext context) { return new NoRollbackFlow() { String __name__ = "prepare-flat-dns"; @Override public void run(final FlowTrigger trigger, Map data) { setDnsOnHost(context.getInventory(), new Completion(trigger) { @Override public void success() { trigger.next(); } @Override public void fail(ErrorCode errorCode) { trigger.fail(errorCode); } }); } }; } public static class SetDnsCmd extends KVMAgentCommands.AgentCommand { public List<String> dns; } public static class SetDnsRsp extends KVMAgentCommands.AgentResponse { } public NetworkServiceProviderType getProviderType() { return FlatNetworkServiceConstant.FLAT_NETWORK_SERVICE_TYPE; } private void setDns(final L3NetworkInventory l3, final List<String> dns, final Completion completion) { final List<String> hostUuids = new Callable<List<String>>() { @Override @Transactional(readOnly = true) public List<String> call() { String sql = "select h.uuid from HostVO h, L2NetworkClusterRefVO ref where h.clusterUuid = ref.clusterUuid" + " and ref.l2NetworkUuid = :l2Uuid and h.status = :hstatus"; TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class); q.setParameter("l2Uuid", l3.getL2NetworkUuid()); q.setParameter("hstatus", HostStatus.Connected); return q.getResultList(); } }.call(); List<KVMHostAsyncHttpCallMsg> msgs = CollectionUtils.transformToList(hostUuids, new Function<KVMHostAsyncHttpCallMsg, String>() { @Override public KVMHostAsyncHttpCallMsg call(String huuid) { SetDnsCmd cmd = new SetDnsCmd(); cmd.dns = dns; KVMHostAsyncHttpCallMsg msg = new KVMHostAsyncHttpCallMsg(); msg.setCommand(cmd); msg.setCommandTimeout(timeoutMgr.getTimeout(cmd.getClass(), "5m")); msg.setPath(SET_DNS_PATH); msg.setHostUuid(huuid); bus.makeTargetServiceIdByResourceUuid(msg, HostConstant.SERVICE_ID, huuid); return msg; } }); bus.send(msgs, new CloudBusListCallBack(completion) { @Override public void run(List<MessageReply> replies) { for (MessageReply reply : replies) { String huuid = hostUuids.get(replies.indexOf(reply)); if (!reply.isSuccess()) { //TODO: GC logger.warn(String.format("failed to apply dns%s to the kvm host[uuid:%s], %s", dns, huuid, reply.getError())); continue; } KVMHostAsyncHttpCallReply re = reply.castReply(); SetDnsRsp rsp = re.toResponse(SetDnsRsp.class); if (!rsp.isSuccess()) { //TODO GC logger.warn(String.format("failed to apply dns%s to the kvm host[uuid:%s], %s", dns, huuid, rsp.getError())); } } completion.success(); } }); } @Override public void addDns(final L3NetworkInventory l3, final List<String> dns, final Completion completion) { setDns(l3, l3.getDns(), completion); } @Override public void removeDns(final L3NetworkInventory l3, List<String> dns, Completion completion) { List<String> newDns = new ArrayList<String>(); newDns.addAll(l3.getDns()); newDns.removeAll(dns); setDns(l3, newDns, completion); } @Override public void applyDnsService(List<DnsStruct> dnsStructList, VmInstanceSpec spec, Completion completion) { completion.success(); } @Override public void releaseDnsService(List<DnsStruct> dnsStructsList, VmInstanceSpec spec, NoErrorCompletion completion) { completion.done(); } }