package org.zstack.network.service.lb;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.Platform;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.SimpleQuery;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.core.db.UpdateQuery;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.core.thread.ChainTask;
import org.zstack.core.thread.SyncTaskChain;
import org.zstack.core.thread.ThreadFacade;
import org.zstack.core.workflow.FlowChainBuilder;
import org.zstack.core.workflow.ShareFlow;
import org.zstack.header.core.Completion;
import org.zstack.header.core.NoErrorCompletion;
import org.zstack.header.core.workflow.*;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.message.APIMessage;
import org.zstack.header.message.Message;
import org.zstack.header.network.l3.L3NetworkVO;
import org.zstack.header.network.service.NetworkServiceL3NetworkRefVO;
import org.zstack.header.vm.*;
import org.zstack.identity.AccountManager;
import org.zstack.network.service.vip.*;
import org.zstack.tag.TagManager;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.DebugUtils;
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 javax.persistence.TypedQuery;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.Arrays.asList;
/**
* Created by frank on 8/8/2015.
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class LoadBalancerBase {
private static final CLogger logger = Utils.getLogger(LoadBalancerBase.class);
@Autowired
private CloudBus bus;
@Autowired
private DatabaseFacade dbf;
@Autowired
private LoadBalancerManager lbMgr;
@Autowired
private ThreadFacade thdf;
@Autowired
private ErrorFacade errf;
@Autowired
private AccountManager acntMgr;
@Autowired
private TagManager tagMgr;
private LoadBalancerVO self;
private String getSyncId() {
return String.format("operate-lb-%s", self.getUuid());
}
protected LoadBalancerInventory getInventory() {
return LoadBalancerInventory.valueOf(self);
}
private LoadBalancerInventory reloadAndGetInventory() {
self = dbf.reload(self);
return getInventory();
}
public LoadBalancerBase(LoadBalancerVO self) {
this.self = self;
}
void handleMessage(Message msg) {
if (msg instanceof APIMessage) {
handleApiMessage((APIMessage) msg);
} else {
handleLocalMessage(msg);
}
}
private void handleLocalMessage(Message msg) {
if (msg instanceof LoadBalancerActiveVmNicMsg) {
handle((LoadBalancerActiveVmNicMsg) msg);
} else if (msg instanceof LoadBalancerDeactiveVmNicMsg) {
handle((LoadBalancerDeactiveVmNicMsg) msg);
} else if (msg instanceof LoadBalancerRemoveVmNicMsg) {
handle((LoadBalancerRemoveVmNicMsg) msg);
} else if (msg instanceof RefreshLoadBalancerMsg) {
handle((RefreshLoadBalancerMsg) msg);
} else if (msg instanceof DeleteLoadBalancerMsg) {
handle((DeleteLoadBalancerMsg) msg);
} else if (msg instanceof DeleteLoadBalancerOnlyMsg) {
handle((DeleteLoadBalancerOnlyMsg) msg);
} else {
bus.dealWithUnknownMessage(msg);
}
}
private void handle(DeleteLoadBalancerOnlyMsg msg) {
DeleteLoadBalancerOnlyReply reply = new DeleteLoadBalancerOnlyReply();
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return getSyncId();
}
@Override
public void run(SyncTaskChain chain) {
if (self.getProviderType() == null) {
// not initialized yet
dbf.remove(self);
bus.reply(msg, reply);
chain.next();
return;
}
LoadBalancerBackend bkd = getBackend();
bkd.destroyLoadBalancer(makeStruct(), new Completion(msg, chain) {
@Override
public void success() {
dbf.remove(self);
bus.reply(msg, reply);
chain.next();
}
@Override
public void fail(ErrorCode errorCode) {
reply.setError(errorCode);
bus.reply(msg, reply);
chain.next();
}
});
}
@Override
public String getName() {
return "delete-load-balancer-only";
}
});
}
private void handle(final DeleteLoadBalancerMsg msg) {
final DeleteLoadBalancerReply reply = new DeleteLoadBalancerReply();
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return getSyncId();
}
@Override
public void run(final SyncTaskChain chain) {
delete(new Completion(msg, chain) {
@Override
public void success() {
bus.reply(msg ,reply);
chain.next();
}
@Override
public void fail(ErrorCode errorCode) {
reply.setError(errorCode);
bus.reply(msg ,reply);
chain.next();
}
});
}
@Override
public String getName() {
return "delete-lb";
}
});
}
private void handle(final RefreshLoadBalancerMsg msg) {
final RefreshLoadBalancerReply reply = new RefreshLoadBalancerReply();
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return getSyncId();
}
@Override
public void run(final SyncTaskChain chain) {
refresh(new Completion(msg, chain) {
@Override
public void success() {
reply.setInventory(getInventory());
bus.reply(msg, reply);
chain.next();
}
@Override
public void fail(ErrorCode errorCode) {
reply.setError(errorCode);
bus.reply(msg, reply);
chain.next();
}
});
}
@Override
public String getName() {
return "refresh-lb";
}
});
}
private void refresh(final Completion completion) {
LoadBalancerBackend bkd = getBackend();
bkd.refresh(makeStruct(), completion);
}
private void handle(final LoadBalancerRemoveVmNicMsg msg) {
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return getSyncId();
}
@Override
public void run(final SyncTaskChain chain) {
final LoadBalancerRemoveVmNicReply reply = new LoadBalancerRemoveVmNicReply();
removeNics(msg.getListenerUuid(), msg.getVmNicUuids(), new Completion(msg, chain) {
@Override
public void success() {
bus.reply(msg, reply);
chain.next();
}
@Override
public void fail(ErrorCode errorCode) {
reply.setError(errorCode);
bus.reply(msg, reply);
chain.next();
}
});
}
@Override
public String getName() {
return "remove-nic-from-lb";
}
});
}
private void checkIfNicIsAdded(List<String> nicUuids) {
List<String> allNicUuids = new ArrayList<String>();
for (LoadBalancerListenerVO l : self.getListeners()) {
allNicUuids.addAll(CollectionUtils.transformToList(l.getVmNicRefs(), new Function<String, LoadBalancerListenerVmNicRefVO>() {
@Override
public String call(LoadBalancerListenerVmNicRefVO arg) {
return arg.getVmNicUuid();
}
}));
}
for (String nicUuid : nicUuids) {
if (!allNicUuids.contains(nicUuid)) {
throw new CloudRuntimeException(String.format("the load balancer[uuid: %s] doesn't have a vm nic[uuid: %s] added", self.getUuid(), nicUuid));
}
}
}
private void handle(final LoadBalancerDeactiveVmNicMsg msg) {
checkIfNicIsAdded(msg.getVmNicUuids());
LoadBalancerListenerVO l = CollectionUtils.find(self.getListeners(), new Function<LoadBalancerListenerVO, LoadBalancerListenerVO>() {
@Override
public LoadBalancerListenerVO call(LoadBalancerListenerVO arg) {
return arg.getUuid().equals(msg.getListenerUuid()) ? arg : null;
}
});
final List<LoadBalancerListenerVmNicRefVO> refs = CollectionUtils.transformToList(l.getVmNicRefs(), new Function<LoadBalancerListenerVmNicRefVO, LoadBalancerListenerVmNicRefVO>() {
@Override
public LoadBalancerListenerVmNicRefVO call(LoadBalancerListenerVmNicRefVO arg) {
return msg.getVmNicUuids().contains(arg.getVmNicUuid()) ? arg : null;
}
});
final LoadBalancerDeactiveVmNicReply reply = new LoadBalancerDeactiveVmNicReply();
FlowChain chain = FlowChainBuilder.newShareFlowChain();
chain.setName(String.format("deactive-vm-nics-on-lb-%s", self.getUuid()));
chain.then(new ShareFlow() {
@Override
public void setup() {
flow(new Flow() {
String __name__ = "set-nics-to-inactive-in-db";
@Override
public void run(FlowTrigger trigger, Map data) {
for (LoadBalancerListenerVmNicRefVO ref : refs) {
ref.setStatus(LoadBalancerVmNicStatus.Inactive);
dbf.update(ref);
}
trigger.next();
}
@Override
public void rollback(FlowRollback trigger, Map data) {
for (LoadBalancerListenerVmNicRefVO ref : refs) {
ref.setStatus(LoadBalancerVmNicStatus.Active);
dbf.update(ref);
}
trigger.rollback();
}
});
flow(new NoRollbackFlow() {
String __name__ = "deactive-nics-on-backend";
@Override
public void run(final FlowTrigger trigger, Map data) {
SimpleQuery<VmNicVO> q = dbf.createQuery(VmNicVO.class);
q.add(VmNicVO_.uuid, Op.IN, CollectionUtils.transformToList(refs, new Function<String, LoadBalancerListenerVmNicRefVO>() {
@Override
public String call(LoadBalancerListenerVmNicRefVO arg) {
return arg.getVmNicUuid();
}
}));
List<VmNicVO> nicvos = q.list();
LoadBalancerBackend bkd = getBackend();
bkd.removeVmNics(makeStruct(), VmNicInventory.valueOf(nicvos), new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
});
done(new FlowDoneHandler(msg) {
@Override
public void handle(Map data) {
bus.reply(msg, reply);
}
});
error(new FlowErrorHandler(msg) {
@Override
public void handle(ErrorCode errCode, Map data) {
reply.setError(errCode);
bus.reply(msg, reply);
}
});
}
}).start();
}
private void activeVmNic(final LoadBalancerActiveVmNicMsg msg, final NoErrorCompletion completion) {
checkIfNicIsAdded(msg.getVmNicUuids());
LoadBalancerListenerVO l = CollectionUtils.find(self.getListeners(), new Function<LoadBalancerListenerVO, LoadBalancerListenerVO>() {
@Override
public LoadBalancerListenerVO call(LoadBalancerListenerVO arg) {
return arg.getUuid().equals(msg.getListenerUuid()) ? arg : null;
}
});
final List<LoadBalancerListenerVmNicRefVO> refs = CollectionUtils.transformToList(l.getVmNicRefs(), new Function<LoadBalancerListenerVmNicRefVO, LoadBalancerListenerVmNicRefVO>() {
@Override
public LoadBalancerListenerVmNicRefVO call(LoadBalancerListenerVmNicRefVO arg) {
return msg.getVmNicUuids().contains(arg.getVmNicUuid()) ? arg : null;
}
});
final LoadBalancerActiveVmNicReply reply = new LoadBalancerActiveVmNicReply();
FlowChain chain = FlowChainBuilder.newShareFlowChain();
chain.setName(String.format("active-vm-nics-on-lb-%s", self.getUuid()));
chain.then(new ShareFlow() {
@Override
public void setup() {
flow(new Flow() {
String __name__ = "set-nics-to-active-in-db";
@Override
public void run(FlowTrigger trigger, Map data) {
for (LoadBalancerListenerVmNicRefVO ref : refs) {
ref.setStatus(LoadBalancerVmNicStatus.Active);
dbf.update(ref);
}
trigger.next();
}
@Override
public void rollback(FlowRollback trigger, Map data) {
for (LoadBalancerListenerVmNicRefVO ref : refs) {
ref.setStatus(LoadBalancerVmNicStatus.Inactive);
dbf.update(ref);
}
trigger.rollback();
}
});
flow(new NoRollbackFlow() {
String __name__ = "active-nics-on-backend";
@Override
public void run(final FlowTrigger trigger, Map data) {
SimpleQuery<VmNicVO> q = dbf.createQuery(VmNicVO.class);
q.add(VmNicVO_.uuid, Op.IN, CollectionUtils.transformToList(refs, new Function<String, LoadBalancerListenerVmNicRefVO>() {
@Override
public String call(LoadBalancerListenerVmNicRefVO arg) {
return arg.getVmNicUuid();
}
}));
List<VmNicVO> nicvos = q.list();
LoadBalancerBackend bkd = getBackend();
bkd.addVmNics(makeStruct(), VmNicInventory.valueOf(nicvos), new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
});
done(new FlowDoneHandler(msg) {
@Override
public void handle(Map data) {
bus.reply(msg, reply);
completion.done();
}
});
error(new FlowErrorHandler(msg) {
@Override
public void handle(ErrorCode errCode, Map data) {
reply.setError(errCode);
bus.reply(msg, reply);
completion.done();
}
});
}
}).start();
}
private void handle(final LoadBalancerActiveVmNicMsg msg) {
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return getSyncId();
}
@Override
public void run(final SyncTaskChain chain) {
activeVmNic(msg, new NoErrorCompletion(msg, chain) {
@Override
public void done() {
chain.next();
}
});
}
@Override
public String getName() {
return "deactive-nic";
}
});
}
private void handleApiMessage(APIMessage msg) {
if (msg instanceof APICreateLoadBalancerListenerMsg) {
handle((APICreateLoadBalancerListenerMsg) msg);
} else if (msg instanceof APIAddVmNicToLoadBalancerMsg) {
handle((APIAddVmNicToLoadBalancerMsg) msg);
} else if (msg instanceof APIRemoveVmNicFromLoadBalancerMsg) {
handle((APIRemoveVmNicFromLoadBalancerMsg) msg);
} else if (msg instanceof APIDeleteLoadBalancerListenerMsg) {
handle((APIDeleteLoadBalancerListenerMsg) msg);
} else if (msg instanceof APIDeleteLoadBalancerMsg) {
handle((APIDeleteLoadBalancerMsg) msg);
} else if (msg instanceof APIRefreshLoadBalancerMsg) {
handle((APIRefreshLoadBalancerMsg) msg);
} else if (msg instanceof APIGetCandidateVmNicsForLoadBalancerMsg) {
handle((APIGetCandidateVmNicsForLoadBalancerMsg) msg);
} else {
bus.dealWithUnknownMessage(msg);
}
}
@Transactional(readOnly = true)
private void handle(APIGetCandidateVmNicsForLoadBalancerMsg msg) {
APIGetCandidateVmNicsForLoadBalancerReply reply = new APIGetCandidateVmNicsForLoadBalancerReply();
String sql = "select vip.peerL3NetworkUuid from VipVO vip where vip.uuid = :uuid";
TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
q.setParameter("uuid", self.getVipUuid());
List<String> ret = q.getResultList();
String peerL3Uuid = ret.isEmpty() ? null : ret.get(0);
if (peerL3Uuid != null) {
// the load balancer has been bound to a private L3 network
sql = "select nic from VmNicVO nic, VmInstanceVO vm where nic.l3NetworkUuid = :l3Uuid and nic.uuid not in (select ref.vmNicUuid from LoadBalancerListenerVmNicRefVO ref" +
" where ref.listenerUuid = :luuid) and nic.vmInstanceUuid = vm.uuid and vm.type = :vmType and vm.state in (:vmStates)";
TypedQuery<VmNicVO> pq = dbf.getEntityManager().createQuery(sql, VmNicVO.class);
pq.setParameter("l3Uuid", peerL3Uuid);
pq.setParameter("luuid", msg.getListenerUuid());
pq.setParameter("vmType", VmInstanceConstant.USER_VM_TYPE);
pq.setParameter("vmStates", asList(VmInstanceState.Running, VmInstanceState.Stopped));
List<VmNicVO> nics = pq.getResultList();
reply.setInventories(VmNicInventory.valueOf(nics));
bus.reply(msg, reply);
return;
}
// the load balancer has not been bound to any private L3 network
sql = "select l3.uuid from L3NetworkVO l3, NetworkServiceL3NetworkRefVO ref where l3.uuid = ref.l3NetworkUuid" +
" and ref.networkServiceType = :type";
q = dbf.getEntityManager().createQuery(sql, String.class);
q.setParameter("type", LoadBalancerConstants.LB_NETWORK_SERVICE_TYPE_STRING);
List<String> l3Uuids = q.getResultList();
if (l3Uuids.isEmpty()) {
reply.setInventories(new ArrayList<>());
bus.reply(msg, reply);
return;
}
sql = "select nic from VmNicVO nic, VmInstanceVO vm where nic.l3NetworkUuid in (select l3.uuid from L3NetworkVO l3, NetworkServiceL3NetworkRefVO ref where l3.uuid = ref.l3NetworkUuid" +
" and ref.networkServiceType = :type) and nic.vmInstanceUuid = vm.uuid and vm.type = :vmType and vm.state in (:vmStates)";
TypedQuery<VmNicVO> nq = dbf.getEntityManager().createQuery(sql, VmNicVO.class);
nq.setParameter("type", LoadBalancerConstants.LB_NETWORK_SERVICE_TYPE_STRING);
nq.setParameter("vmType", VmInstanceConstant.USER_VM_TYPE);
nq.setParameter("vmStates", asList(VmInstanceState.Running, VmInstanceState.Stopped));
List<VmNicVO> nics = nq.getResultList();
reply.setInventories(VmNicInventory.valueOf(nics));
bus.reply(msg, reply);
}
private void handle(final APIRefreshLoadBalancerMsg msg) {
final APIRefreshLoadBalancerEvent evt = new APIRefreshLoadBalancerEvent(msg.getId());
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return getSyncId();
}
@Override
public void run(final SyncTaskChain chain) {
refresh(new Completion(msg, chain) {
@Override
public void success() {
evt.setInventory(getInventory());
bus.publish(evt);
chain.next();
}
@Override
public void fail(ErrorCode errorCode) {
evt.setError(errorCode);
bus.publish(evt);
chain.next();
}
});
}
@Override
public String getName() {
return "refresh-lb";
}
});
}
private void handle(final APIDeleteLoadBalancerMsg msg) {
final APIDeleteLoadBalancerEvent evt = new APIDeleteLoadBalancerEvent(msg.getId());
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return getSyncId();
}
@Override
public void run(final SyncTaskChain chain) {
delete(new Completion(msg, chain) {
@Override
public void success() {
bus.publish(evt);
chain.next();
}
@Override
public void fail(ErrorCode errorCode) {
evt.setError(errorCode);
bus.publish(evt);
chain.next();
}
});
}
@Override
public String getName() {
return "delete-lb";
}
});
}
private void delete(final Completion completion) {
FlowChain chain = FlowChainBuilder.newShareFlowChain();
chain.setName(String.format("delete-lb-%s", self.getUuid()));
chain.then(new ShareFlow() {
@Override
public void setup() {
flow(new NoRollbackFlow() {
String __name__ = "delete-lb";
@Override
public void run(final FlowTrigger trigger, Map data) {
if (self.getProviderType() == null) {
trigger.next();
// not initialized yet
return;
}
LoadBalancerBackend bkd = getBackend();
bkd.destroyLoadBalancer(makeStruct(), new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
});
flow(new NoRollbackFlow() {
String __name__ = "release-vip";
@Override
public void run(FlowTrigger trigger, Map data) {
new Vip(self.getVipUuid()).release(new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
});
done(new FlowDoneHandler(completion) {
@Override
public void handle(Map data) {
dbf.remove(self);
completion.success();
}
});
error(new FlowErrorHandler(completion) {
@Override
public void handle(ErrorCode errCode, Map data) {
completion.fail(errCode);
}
});
}
}).start();
}
private void handle(final APIDeleteLoadBalancerListenerMsg msg) {
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return getSyncId();
}
@Override
public void run(final SyncTaskChain chain) {
deleteListener(msg, new NoErrorCompletion(msg, chain) {
@Override
public void done() {
chain.next();
}
});
}
@Override
public String getName() {
return "delete-listener";
}
});
}
private LoadBalancerStruct removeListenerStruct(LoadBalancerListenerInventory listener) {
LoadBalancerStruct s = makeStruct();
for (LoadBalancerListenerInventory l : s.getListeners()) {
if (l.getUuid().equals(listener.getUuid())) {
l.setVmNicRefs(new ArrayList<>());
}
}
return s;
}
private void deleteListener(APIDeleteLoadBalancerListenerMsg msg, final NoErrorCompletion completion) {
final APIDeleteLoadBalancerListenerEvent evt = new APIDeleteLoadBalancerListenerEvent(msg.getId());
final LoadBalancerListenerVO vo = dbf.findByUuid(msg.getUuid(), LoadBalancerListenerVO.class);
if (vo == null) {
evt.setInventory(getInventory());
bus.publish(evt);
completion.done();
return;
}
if (!needAction()) {
dbf.remove(vo);
evt.setInventory(reloadAndGetInventory());
bus.publish(evt);
completion.done();
return;
}
LoadBalancerListenerInventory listener = LoadBalancerListenerInventory.valueOf(vo);
LoadBalancerBackend bkd = getBackend();
bkd.removeListener(removeListenerStruct(listener), listener, new Completion(msg, completion) {
@Override
public void success() {
dbf.remove(vo);
evt.setInventory(reloadAndGetInventory());
bus.publish(evt);
completion.done();
}
@Override
public void fail(ErrorCode errorCode) {
evt.setError(errorCode);
bus.publish(evt);
completion.done();
}
});
}
private void handle(final APIRemoveVmNicFromLoadBalancerMsg msg) {
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return getSyncId();
}
@Override
public void run(final SyncTaskChain chain) {
removeNic(msg, new NoErrorCompletion(msg, chain) {
@Override
public void done() {
chain.next();
}
});
}
@Override
public String getName() {
return "remove-nic";
}
});
}
private LoadBalancerStruct removeNicStruct(String listenerUuid, List<String> nicUuids) {
LoadBalancerStruct s = makeStruct();
Optional<LoadBalancerListenerInventory> opt = s.getListeners().stream().filter(it -> it.getUuid().equals(listenerUuid)).findAny();
DebugUtils.Assert(opt.isPresent(), String.format("cannot find listener[uuid:%s]", listenerUuid));
LoadBalancerListenerInventory l = opt.get();
l.getVmNicRefs().removeIf(loadBalancerListenerVmNicRefInventory -> nicUuids.contains(loadBalancerListenerVmNicRefInventory.getVmNicUuid()));
return s;
}
private void removeNics(String listenerUuid, final List<String> vmNicUuids, final Completion completion) {
SimpleQuery<VmNicVO> q = dbf.createQuery(VmNicVO.class);
q.add(VmNicVO_.uuid, Op.IN, vmNicUuids);
List<VmNicVO> vos = q.list();
List<VmNicInventory> nics = VmNicInventory.valueOf(vos);
LoadBalancerBackend bkd = getBackend();
bkd.removeVmNics(removeNicStruct(listenerUuid, vmNicUuids), nics, new Completion(completion) {
@Override
public void success() {
UpdateQuery.New(LoadBalancerListenerVmNicRefVO.class)
.condAnd(LoadBalancerListenerVmNicRefVO_.vmNicUuid, Op.IN, vmNicUuids)
.condAnd(LoadBalancerListenerVmNicRefVO_.listenerUuid, Op.EQ, listenerUuid)
.delete();
completion.success();
}
@Override
public void fail(ErrorCode errorCode) {
completion.fail(errorCode);
}
});
}
private void removeNic(APIRemoveVmNicFromLoadBalancerMsg msg, final NoErrorCompletion completion) {
final APIRemoveVmNicFromLoadBalancerEvent evt = new APIRemoveVmNicFromLoadBalancerEvent(msg.getId());
removeNics(msg.getListenerUuid(), msg.getVmNicUuids(), new Completion(msg, completion) {
@Override
public void success() {
evt.setInventory(reloadAndGetInventory());
bus.publish(evt);
completion.done();
}
@Override
public void fail(ErrorCode errorCode) {
evt.setError(errorCode);
bus.publish(evt);
completion.done();
}
});
}
@Transactional(readOnly = true)
private String findProviderTypeByVmNicUuid(String nicUuid) {
String sql = "select l3 from L3NetworkVO l3, VmNicVO nic where nic.l3NetworkUuid = l3.uuid and nic.uuid = :uuid";
TypedQuery<L3NetworkVO> q = dbf.getEntityManager().createQuery(sql, L3NetworkVO.class);
q.setParameter("uuid", nicUuid);
L3NetworkVO l3 = q.getSingleResult();
for (NetworkServiceL3NetworkRefVO ref : l3.getNetworkServices()) {
if (LoadBalancerConstants.LB_NETWORK_SERVICE_TYPE_STRING.equals(ref.getNetworkServiceType())) {
sql = "select p.type from NetworkServiceProviderVO p where p.uuid = :uuid";
TypedQuery<String> nq = dbf.getEntityManager().createQuery(sql, String.class);
nq.setParameter("uuid", ref.getNetworkServiceProviderUuid());
return nq.getSingleResult();
}
}
return null;
}
private void handle(final APIAddVmNicToLoadBalancerMsg msg) {
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return getSyncId();
}
@Override
public void run(final SyncTaskChain chain) {
addVmNicToListener(msg, new NoErrorCompletion(chain) {
@Override
public void done() {
chain.next();
}
});
}
@Override
public String getName() {
return getSyncSignature();
}
});
}
private void addVmNicToListener(final APIAddVmNicToLoadBalancerMsg msg, final NoErrorCompletion completion) {
final APIAddVmNicToLoadBalancerEvent evt = new APIAddVmNicToLoadBalancerEvent(msg.getId());
final String providerType = findProviderTypeByVmNicUuid(msg.getVmNicUuids().get(0));
if (providerType == null) {
throw new OperationFailureException(operr("the L3 network of vm nic[uuid:%s] doesn't have load balancer service enabled", msg.getVmNicUuids().get(0)));
}
SimpleQuery<VmNicVO> q = dbf.createQuery(VmNicVO.class);
q.add(VmNicVO_.uuid, Op.IN, msg.getVmNicUuids());
List<VmNicVO> nicVOs = q.list();
final List<VmNicInventory> nics = VmNicInventory.valueOf(nicVOs);
FlowChain chain = FlowChainBuilder.newShareFlowChain();
chain.setName(String.format("add-vm-nic-to-lb-listener-%s", msg.getListenerUuid()));
chain.then(new ShareFlow() {
List<LoadBalancerListenerVmNicRefVO> refs = new ArrayList<LoadBalancerListenerVmNicRefVO>();
boolean init = false;
@Override
public void setup() {
flow(new Flow() {
String __name__ = "check-provider-type";
@Override
public void run(FlowTrigger trigger, Map data) {
if (self.getProviderType() == null) {
self.setProviderType(providerType);
self = dbf.updateAndRefresh(self);
init = true;
} else {
if (!providerType.equals(self.getProviderType())) {
throw new OperationFailureException(operr("service provider type mismatching. The load balancer[uuid:%s] is provided by the service provider[type:%s]," +
" but the L3 network of vm nic[uuid:%s] is enabled with the service provider[type: %s]", self.getUuid(), self.getProviderType(),
msg.getVmNicUuids().get(0), providerType));
}
}
trigger.next();
}
@Override
public void rollback(FlowRollback trigger, Map data) {
if (init) {
self = dbf.reload(self);
self.setProviderType(null);
dbf.update(self);
}
trigger.rollback();
}
});
flow(new Flow() {
String __name__ = "write-nic-to-db";
boolean s = false;
@Override
public void run(FlowTrigger trigger, Map data) {
for (String nicUuid : msg.getVmNicUuids()) {
LoadBalancerListenerVmNicRefVO ref = new LoadBalancerListenerVmNicRefVO();
ref.setListenerUuid(msg.getListenerUuid());
ref.setVmNicUuid(nicUuid);
ref.setStatus(LoadBalancerVmNicStatus.Pending);
refs.add(ref);
}
dbf.persistCollection(refs);
s = true;
trigger.next();
}
@Override
public void rollback(FlowRollback trigger, Map data) {
if (s) {
dbf.removeCollection(refs, LoadBalancerListenerVmNicRefVO.class);
}
trigger.rollback();
}
});
flow(new NoRollbackFlow() {
String __name__ = "add-nic-to-lb";
@Override
public void run(final FlowTrigger trigger, Map data) {
LoadBalancerBackend bkd = getBackend();
LoadBalancerStruct s = makeStruct();
s.setInit(init);
bkd.addVmNics(s, nics, new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
});
done(new FlowDoneHandler(msg, completion) {
@Override
public void handle(Map data) {
for (LoadBalancerListenerVmNicRefVO ref : refs) {
ref.setStatus(LoadBalancerVmNicStatus.Active);
}
dbf.updateCollection(refs);
evt.setInventory(LoadBalancerListenerInventory.valueOf(dbf.findByUuid(msg.getListenerUuid(), LoadBalancerListenerVO.class)));
bus.publish(evt);
completion.done();
}
});
error(new FlowErrorHandler(msg, completion) {
@Override
public void handle(ErrorCode errCode, Map data) {
evt.setError(errCode);
bus.publish(evt);
completion.done();
}
});
}
}).start();
}
private boolean needAction() {
if (self.getProviderType() == null) {
return false;
}
LoadBalancerListenerVmNicRefVO activeNic = CollectionUtils.find(self.getListeners(), new Function<LoadBalancerListenerVmNicRefVO, LoadBalancerListenerVO>() {
@Override
public LoadBalancerListenerVmNicRefVO call(LoadBalancerListenerVO arg) {
for (LoadBalancerListenerVmNicRefVO ref : arg.getVmNicRefs()) {
if (ref.getStatus() == LoadBalancerVmNicStatus.Active || ref.getStatus() == LoadBalancerVmNicStatus.Pending) {
return ref;
}
}
return null;
}
});
if (activeNic == null) {
return false;
}
return true;
}
private LoadBalancerBackend getBackend() {
DebugUtils.Assert(self.getProviderType() != null, "providerType cannot be null");
return lbMgr.getBackend(self.getProviderType());
}
private LoadBalancerStruct makeStruct() {
LoadBalancerStruct struct = new LoadBalancerStruct();
struct.setLb(reloadAndGetInventory());
List<String> activeNicUuids = new ArrayList<String>();
for (LoadBalancerListenerVO l : self.getListeners()) {
activeNicUuids.addAll(CollectionUtils.transformToList(l.getVmNicRefs(), new Function<String, LoadBalancerListenerVmNicRefVO>() {
@Override
public String call(LoadBalancerListenerVmNicRefVO arg) {
return arg.getStatus() == LoadBalancerVmNicStatus.Active || arg.getStatus() == LoadBalancerVmNicStatus.Pending ? arg.getVmNicUuid() : null;
}
}));
}
if (activeNicUuids.isEmpty()) {
struct.setVmNics(new HashMap<String, VmNicInventory>());
} else {
SimpleQuery<VmNicVO> nq = dbf.createQuery(VmNicVO.class);
nq.add(VmNicVO_.uuid, Op.IN, activeNicUuids);
List<VmNicVO> nicvos = nq.list();
Map<String, VmNicInventory> m = new HashMap<String, VmNicInventory>();
for (VmNicVO n : nicvos) {
m.put(n.getUuid(), VmNicInventory.valueOf(n));
}
struct.setVmNics(m);
}
struct.setListeners(LoadBalancerListenerInventory.valueOf(self.getListeners()));
return struct;
}
private void handle(final APICreateLoadBalancerListenerMsg msg) {
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return getSyncId();
}
@Override
public void run(final SyncTaskChain chain) {
createListener(msg, new NoErrorCompletion(chain) {
@Override
public void done() {
chain.next();
}
});
}
@Override
public String getName() {
return "create-listener";
}
});
}
private void createListener(final APICreateLoadBalancerListenerMsg msg, final NoErrorCompletion completion) {
final APICreateLoadBalancerListenerEvent evt = new APICreateLoadBalancerListenerEvent(msg.getId());
LoadBalancerListenerVO vo = new LoadBalancerListenerVO();
vo.setLoadBalancerUuid(self.getUuid());
vo.setUuid(msg.getResourceUuid() == null ? Platform.getUuid() : msg.getResourceUuid());
vo.setDescription(vo.getDescription());
vo.setName(msg.getName());
vo.setInstancePort(msg.getInstancePort());
vo.setLoadBalancerPort(msg.getLoadBalancerPort());
vo.setProtocol(msg.getProtocol());
vo = dbf.persistAndRefresh(vo);
acntMgr.createAccountResourceRef(msg.getSession().getAccountUuid(), vo.getUuid(), LoadBalancerListenerVO.class);
tagMgr.createNonInherentSystemTags(msg.getSystemTags(), vo.getUuid(), LoadBalancerListenerVO.class.getSimpleName());
evt.setInventory(LoadBalancerListenerInventory.valueOf(vo));
bus.publish(evt);
completion.done();
}
}