package org.zstack.network.service.eip;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.header.Component;
import org.zstack.header.core.Completion;
import org.zstack.header.core.NoErrorCompletion;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.network.l3.L3NetworkInventory;
import org.zstack.header.network.service.NetworkServiceProviderType;
import org.zstack.header.network.service.NetworkServiceType;
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.header.vm.VmNicVO;
import org.zstack.network.service.AbstractNetworkServiceExtension;
import org.zstack.network.service.vip.VipInventory;
import org.zstack.network.service.vip.VipVO;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.function.Function;
import org.zstack.utils.function.FunctionNoArg;
import org.zstack.utils.logging.CLogger;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import java.util.*;
/**
*/
public class EipExtension extends AbstractNetworkServiceExtension implements Component {
private static final CLogger logger = Utils.getLogger(EipExtension.class);
@Autowired
private EipManager eipMgr;
private static final String SUCCESS = EipExtension.class.getName();
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public NetworkServiceType getNetworkServiceType() {
return EipConstant.EIP_TYPE;
}
@Transactional
private EipStruct eipVOtoEipStruct(EipVO vo) {
String sql = "select nic from VmNicVO nic where nic.uuid = :uuid";
TypedQuery<VmNicVO> q = dbf.getEntityManager().createQuery(sql, VmNicVO.class);
q.setParameter("uuid", vo.getVmNicUuid());
VmNicVO nicvo = q.getSingleResult();
sql = "select vip from VipVO vip where vip.uuid = :uuid";
TypedQuery<VipVO> vq = dbf.getEntityManager().createQuery(sql, VipVO.class);
vq.setParameter("uuid", vo.getVipUuid());
VipVO vipvo = vq.getSingleResult();
EipStruct struct = new EipStruct();
struct.setNic(VmNicInventory.valueOf(nicvo));
struct.setVip(VipInventory.valueOf(vipvo));
struct.setEip(EipInventory.valueOf(vo));
struct.setSnatInboundTraffic(EipGlobalConfig.SNAT_INBOUND_TRAFFIC.value(Boolean.class));
return struct;
}
private Map<String, List<EipStruct>> workOutEipStruct(VmInstanceSpec spec) {
Map<NetworkServiceProviderType, List<L3NetworkInventory>> map = getNetworkServiceProviderMap(EipConstant.EIP_TYPE, spec.getL3Networks());
Map<String, List<EipStruct>> ret = new HashMap<String, List<EipStruct>>();
for (Map.Entry<NetworkServiceProviderType, List<L3NetworkInventory>> e : map.entrySet()) {
List<EipStruct> structs = new ArrayList<EipStruct>();
for (final L3NetworkInventory l3 : e.getValue()) {
final VmNicInventory nic = CollectionUtils.find(spec.getDestNics(), new Function<VmNicInventory, VmNicInventory>() {
@Override
public VmNicInventory call(VmNicInventory arg) {
if (arg.getL3NetworkUuid().equals(l3.getUuid())) {
return arg;
}
return null;
}
});
List<EipVO> evos = new FunctionNoArg<List<EipVO>>() {
@Override
@Transactional
public List<EipVO> call() {
String sql = "select eip from EipVO eip, VmNicVO nic where nic.l3NetworkUuid = :l3uuid and nic.uuid = eip.vmNicUuid and nic.uuid = :nicUuid";
Query q = dbf.getEntityManager().createQuery(sql);
q.setParameter("l3uuid", l3.getUuid());
q.setParameter("nicUuid", nic.getUuid());
return q.getResultList();
}
}.call();
if (evos.isEmpty()) {
continue;
}
for (EipVO evo : evos) {
structs.add(eipVOtoEipStruct(evo));
}
}
ret.put(e.getKey().toString(), structs);
}
return ret;
}
private void applyNetworkService(final String providerType, final Iterator<EipStruct> it, final Map<String, List<EipStruct>> applieds, final Completion completion) {
if (!it.hasNext()) {
completion.success();
return;
}
final EipStruct struct = it.next();
eipMgr.attachEip(struct, providerType, new Completion(completion) {
private void addAppliedStruct() {
List<EipStruct> s = applieds.get(providerType);
if (s == null) {
s = new ArrayList<EipStruct>();
applieds.put(providerType, s);
}
s.add(struct);
logger.debug(String.format("successfully applied eip[uuid:%s, ip:%s] for vm nic[uuid:%s]", struct.getEip().getUuid(),
struct.getVip().getIp(), struct.getNic().getUuid()));
}
@Override
public void success() {
addAppliedStruct();
applyNetworkService(providerType, it, applieds, completion);
}
@Override
public void fail(ErrorCode errorCode) {
completion.fail(errorCode);
}
});
}
private void applyNetworkService(final Iterator<Map.Entry<String, List<EipStruct>>> it, final Map<String, List<EipStruct>> applieds, final Completion completion) {
if (!it.hasNext()) {
completion.success();
return;
}
Map.Entry<String, List<EipStruct>> e = it.next();
String providerType = e.getKey();
applyNetworkService(providerType, e.getValue().iterator(), applieds, new Completion(completion) {
@Override
public void success() {
applyNetworkService(it, applieds, completion);
}
@Override
public void fail(ErrorCode errorCode) {
completion.fail(errorCode);
}
});
}
@Override
public void applyNetworkService(VmInstanceSpec spec, Map<String, Object> data, Completion completion) {
// For new created vm, there is no eip
if (spec.getCurrentVmOperation() == VmInstanceConstant.VmOperation.NewCreate) {
completion.success();
return;
}
Map<String, List<EipStruct>> map = workOutEipStruct(spec);
Map<String, List<EipStruct>> applieds = new HashMap<String, List<EipStruct>>();
data.put(SUCCESS, applieds);
applyNetworkService(map.entrySet().iterator(), applieds, completion);
}
private void releaseNetworkService(final Iterator<Map.Entry<String, List<EipStruct>>> it, final boolean detachInDb, final NoErrorCompletion completion) {
if (!it.hasNext()) {
completion.done();
return;
}
Map.Entry<String, List<EipStruct>> e = it.next();
releaseNetworkService(e.getValue().iterator(), e.getKey(), detachInDb, new NoErrorCompletion(completion) {
@Override
public void done() {
releaseNetworkService(it, detachInDb, completion);
}
});
}
private void releaseNetworkService(final Iterator<EipStruct> it, final String providerType, final boolean detachInDb, final NoErrorCompletion completion) {
if (!it.hasNext()) {
completion.done();
return;
}
final EipStruct struct = it.next();
Completion cop = new Completion(completion) {
@Override
public void success() {
logger.debug(String.format("successfully released eip[uuid:%s, ip:%s] for vm[uuid:%s, nic uuid:%s]",
struct.getEip().getUuid(), struct.getVip().getIp(), struct.getNic().getVmInstanceUuid(), struct.getNic().getUuid()));
releaseNetworkService(it, providerType, detachInDb, completion);
}
@Override
public void fail(ErrorCode errorCode) {
String err = String.format("failed to release eip[uuid:%s, ip:%s, vm nic uuid:%s] on service provider[%s], service provider should handle the failure, %s",
struct.getEip().getUuid(), struct.getVip().getIp(), struct.getNic().getUuid(), providerType, errorCode);
logger.warn(err);
releaseNetworkService(it, providerType, detachInDb, completion);
}
};
if (detachInDb) {
eipMgr.detachEipAndUpdateDb(struct, providerType, cop);
} else {
eipMgr.detachEip(struct, providerType, cop);
}
}
@Override
public void releaseNetworkService(VmInstanceSpec spec, Map<String, Object> data, NoErrorCompletion completion) {
// For new created vm, there is no eip
if (spec.getCurrentVmOperation() == VmInstanceConstant.VmOperation.NewCreate) {
completion.done();
return;
}
Map<String, List<EipStruct>> map = null;
if (data.containsKey(SUCCESS)) {
map = (Map<String, List<EipStruct>>) data.get(SUCCESS);
} else {
map = workOutEipStruct(spec);
}
if (map.isEmpty()) {
completion.done();
return;
}
boolean updateDb = spec.getCurrentVmOperation() == VmOperation.Destroy || spec.getCurrentVmOperation() == VmOperation.DetachNic;
releaseNetworkService(map.entrySet().iterator(), updateDb, completion);
}
}