package org.zstack.network.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.MessageSafe;
import org.zstack.core.db.SimpleQuery;
import org.zstack.header.Component;
import org.zstack.header.Service;
import org.zstack.header.core.Completion;
import org.zstack.header.core.NoErrorCompletion;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.message.Message;
import org.zstack.header.network.l3.L3NetworkDnsVO;
import org.zstack.header.network.l3.L3NetworkDnsVO_;
import org.zstack.header.network.l3.L3NetworkInventory;
import org.zstack.header.network.l3.L3NetworkVO;
import org.zstack.header.network.service.*;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import java.util.*;
import static org.zstack.utils.CollectionDSL.list;
/**
* Created with IntelliJ IDEA.
* User: frank
* Time: 7:53 PM
* To change this template use File | Settings | File Templates.
*/
//TODO: implement remove dns when dns is removed from database
public class DnsExtension extends AbstractNetworkServiceExtension implements Component, Service {
private static final CLogger logger = Utils.getLogger(DnsExtension.class);
private Map<NetworkServiceProviderType, NetworkServiceDnsBackend> dnsBackends = new HashMap<NetworkServiceProviderType, NetworkServiceDnsBackend>();
private final String RESULT = String.format("result.%s", DnsExtension.class.getName());
@Autowired
private CloudBus bus;
public NetworkServiceType getNetworkServiceType() {
return NetworkServiceType.DNS;
}
private void doDns(final Iterator<Map.Entry<NetworkServiceDnsBackend, List<DnsStruct>>> it, final VmInstanceSpec spec, final Completion complete) {
if (!it.hasNext()) {
complete.success();
return;
}
Map.Entry<NetworkServiceDnsBackend, List<DnsStruct>> e = it.next();
NetworkServiceDnsBackend bkd = e.getKey();
List<DnsStruct> structs = e.getValue();
logger.debug(String.format("%s is applying DNS service", bkd.getClass().getName()));
bkd.applyDnsService(structs, spec, new Completion(complete) {
@Override
public void success() {
doDns(it, spec, complete);
}
@Override
public void fail(ErrorCode errorCode) {
complete.fail(errorCode);
}
});
}
@Override
public void applyNetworkService(VmInstanceSpec spec, Map<String, Object> data, Completion complete) {
Map<NetworkServiceDnsBackend, List<DnsStruct>> entries = workoutDns(spec);
data.put(RESULT, entries);
doDns(entries.entrySet().iterator(), spec, complete);
}
@Override
public void releaseNetworkService(VmInstanceSpec spec, Map<String, Object> data, NoErrorCompletion completion) {
completion.done();
}
private void populateExtensions() {
for (NetworkServiceDnsBackend extp : pluginRgty.getExtensionList(NetworkServiceDnsBackend.class)) {
NetworkServiceDnsBackend old = dnsBackends.get(extp.getProviderType());
if (old != null) {
throw new CloudRuntimeException(String.format("duplicate NetworkServiceDnsBackend[%s, %s] for type[%s]",
extp.getClass().getName(), old.getClass().getName(), extp.getProviderType()));
}
dnsBackends.put(extp.getProviderType(), extp);
}
}
private List<String> getDnsOnL3(String l3NetworkUuid) {
SimpleQuery<L3NetworkDnsVO> q = dbf.createQuery(L3NetworkDnsVO.class);
q.select(L3NetworkDnsVO_.dns);
q.add(L3NetworkDnsVO_.l3NetworkUuid, SimpleQuery.Op.EQ, l3NetworkUuid);
return q.listValue();
}
private DnsStruct makeDnsStruct(VmInstanceSpec spec, L3NetworkInventory l3) {
List<String> dns = getDnsOnL3(l3.getUuid());
DnsStruct struct = new DnsStruct();
struct.setDns(dns);
struct.setL3Network(l3);
return struct;
}
private Map<NetworkServiceDnsBackend, List<DnsStruct>> workoutDns(VmInstanceSpec spec) {
Map<NetworkServiceDnsBackend, List<DnsStruct>> map = new HashMap<NetworkServiceDnsBackend, List<DnsStruct>>();
Map<NetworkServiceProviderType, List<L3NetworkInventory>> providerMap = getNetworkServiceProviderMap(NetworkServiceType.DNS, spec.getL3Networks());
for (Map.Entry<NetworkServiceProviderType, List<L3NetworkInventory>> e : providerMap.entrySet()) {
NetworkServiceProviderType ptype = e.getKey();
List<DnsStruct> lst = new ArrayList<DnsStruct>();
for (L3NetworkInventory l3 : e.getValue()) {
lst.add(makeDnsStruct(spec, l3));
}
NetworkServiceDnsBackend bkd = dnsBackends.get(ptype);
if (bkd == null) {
throw new CloudRuntimeException(String.format("unable to find NetworkServiceDnsBackend[provider type: %s]", ptype));
}
map.put(bkd, lst);
if (logger.isTraceEnabled()) {
logger.trace(String.format("DNS Backend[%s] is about to apply entries: \n%s", bkd.getClass().getName(), lst));
}
}
return map;
}
@Override
public boolean start() {
populateExtensions();
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
@MessageSafe
public void handleMessage(Message msg) {
if (msg instanceof AddDnsMsg) {
handle((AddDnsMsg) msg);
} else if (msg instanceof RemoveDnsMsg) {
handle((RemoveDnsMsg) msg);
} else {
bus.dealWithUnknownMessage(msg);
}
}
private void handle(final RemoveDnsMsg msg) {
final RemoveDnsReply reply = new RemoveDnsReply();
L3NetworkInventory l3 = L3NetworkInventory.valueOf(dbf.findByUuid(msg.getL3NetworkUuid(), L3NetworkVO.class));
NetworkServiceProviderType ptype = getNetworkServiceProviderType(NetworkServiceType.DNS, l3);
if (ptype == null) {
// backends don't need to be informed
bus.reply(msg, reply);
return;
}
NetworkServiceDnsBackend bkd = dnsBackends.get(ptype);
bkd.removeDns(l3, list(msg.getDns()), new Completion(msg) {
@Override
public void success() {
bus.reply(msg, reply);
}
@Override
public void fail(ErrorCode errorCode) {
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
private void handle(final AddDnsMsg msg) {
final AddDnsReply reply = new AddDnsReply();
L3NetworkInventory l3 = L3NetworkInventory.valueOf(dbf.findByUuid(msg.getL3NetworkUuid(), L3NetworkVO.class));
NetworkServiceProviderType ptype = getNetworkServiceProviderType(NetworkServiceType.DNS, l3);
if (ptype == null) {
// backends don't need to be informed
bus.reply(msg, reply);
return;
}
NetworkServiceDnsBackend bkd = dnsBackends.get(ptype);
bkd.addDns(l3, list(msg.getDns()), new Completion(msg) {
@Override
public void success() {
bus.reply(msg, reply);
}
@Override
public void fail(ErrorCode errorCode) {
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
@Override
public String getId() {
return bus.makeLocalServiceId(NetworkServiceConstants.DNS_SERVICE_ID);
}
@Override
public int getSyncLevel() {
return 0;
}
@Override
public List<String> getAliasIds() {
return null;
}
}