package org.zstack.network.l2;
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.cascade.CascadeConstant;
import org.zstack.core.cascade.CascadeFacade;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusListCallBack;
import org.zstack.core.db.*;
import org.zstack.core.db.SimpleQuery.Op;
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.header.apimediator.ApiMessageInterceptionException;
import org.zstack.header.core.NoErrorCompletion;
import org.zstack.header.core.NopeCompletion;
import org.zstack.header.core.workflow.*;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.errorcode.SysErrors;
import org.zstack.core.inventory.InventoryFacade;
import org.zstack.core.workflow.*;
import org.zstack.header.core.Completion;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.host.*;
import org.zstack.header.message.*;
import org.zstack.header.network.l2.*;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import javax.persistence.TypedQuery;
import java.util.*;
import static java.util.Arrays.asList;
import static org.zstack.core.Platform.argerr;
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class L2NoVlanNetwork implements L2Network {
private static final CLogger logger = Utils.getLogger(L2NoVlanNetwork.class);
@Autowired
protected L2NetworkExtensionPointEmitter extpEmitter;
@Autowired
protected CloudBus bus;
@Autowired
protected DatabaseFacade dbf;
@Autowired
protected L2NetworkManager l2Mgr;
@Autowired
protected InventoryFacade inventoryMgr;
@Autowired
protected CascadeFacade casf;
@Autowired
protected ErrorFacade errf;
@Autowired
protected ThreadFacade thdf;
protected L2NetworkVO self;
public L2NoVlanNetwork(L2NetworkVO self) {
this.self = self;
}
@Override
public void deleteHook() {
}
protected L2NetworkInventory getSelfInventory() {
return L2NetworkInventory.valueOf(self);
}
@Override
public void handleMessage(Message msg) {
try {
if (msg instanceof APIMessage) {
handleApiMessage((APIMessage) msg);
} else {
handleLocalMessage(msg);
}
} catch (Exception e) {
bus.logExceptionWithMessageDump(msg, e);
bus.replyErrorByMessageType(msg, e);
}
}
private void handleLocalMessage(Message msg) {
if (msg instanceof L2NetworkDeletionMsg) {
handle((L2NetworkDeletionMsg) msg);
} else if (msg instanceof CheckL2NetworkOnHostMsg) {
handle((CheckL2NetworkOnHostMsg) msg);
} else if (msg instanceof PrepareL2NetworkOnHostMsg) {
handle((PrepareL2NetworkOnHostMsg) msg);
} else if (msg instanceof DetachL2NetworkFromClusterMsg) {
handle((DetachL2NetworkFromClusterMsg) msg);
} else if (msg instanceof DeleteL2NetworkMsg) {
handle((DeleteL2NetworkMsg) msg);
} else if (msg instanceof L2NetworkDetachFromClusterMsg) {
handle((L2NetworkDetachFromClusterMsg) msg);
} else {
bus.dealWithUnknownMessage(msg);
}
}
private void handle(L2NetworkDetachFromClusterMsg msg) {
L2NetworkDetachFromClusterReply reply = new L2NetworkDetachFromClusterReply();
String issuer = L2NetworkVO.class.getSimpleName();
List<L2NetworkDetachStruct> ctx = new ArrayList<L2NetworkDetachStruct>();
L2NetworkDetachStruct struct = new L2NetworkDetachStruct();
struct.setClusterUuid(msg.getClusterUuid());
struct.setL2NetworkUuid(msg.getL2NetworkUuid());
ctx.add(struct);
casf.asyncCascade(L2NetworkConstant.DETACH_L2NETWORK_CODE, issuer, ctx, new Completion(msg) {
@Override
public void success() {
logger.debug(String.format("successfully detached L2Network[uuid:%s] to cluster [uuid:%s]", self.getUuid(), msg.getClusterUuid()));
self = dbf.reload(self);
bus.reply(msg, reply);
}
@Override
public void fail(ErrorCode errorCode) {
reply.setError(errorCode);
bus.reply(msg, reply);
}
});
}
private void handle(DeleteL2NetworkMsg msg) {
DeleteL2NetworkReply reply = new DeleteL2NetworkReply();
final String issuer = L2NetworkVO.class.getSimpleName();
final List<L2NetworkInventory> ctx = L2NetworkInventory.valueOf(Arrays.asList(self));
FlowChain chain = FlowChainBuilder.newSimpleFlowChain();
chain.setName(String.format("delete-l2Network-%s", msg.getL2NetworkUuid()));
if (msg.isForceDelete() == false) {
chain.then(new NoRollbackFlow() {
@Override
public void run(final FlowTrigger trigger, Map data) {
casf.asyncCascade(CascadeConstant.DELETION_CHECK_CODE, issuer, ctx, new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
}).then(new NoRollbackFlow() {
@Override
public void run(final FlowTrigger trigger, Map data) {
casf.asyncCascade(CascadeConstant.DELETION_DELETE_CODE, issuer, ctx, new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
});
} else {
chain.then(new NoRollbackFlow() {
@Override
public void run(final FlowTrigger trigger, Map data) {
casf.asyncCascade(CascadeConstant.DELETION_FORCE_DELETE_CODE, issuer, ctx, new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
});
}
chain.done(new FlowDoneHandler(msg) {
@Override
public void handle(Map data) {
casf.asyncCascadeFull(CascadeConstant.DELETION_CLEANUP_CODE, issuer, ctx, new NopeCompletion());
bus.reply(msg, reply);
}
}).error(new FlowErrorHandler(msg) {
@Override
public void handle(ErrorCode errCode, Map data) {
reply.setError(errf.instantiateErrorCode(SysErrors.DELETE_RESOURCE_ERROR, errCode));
bus.reply(msg, reply);
}
}).start();
}
private void handle(DetachL2NetworkFromClusterMsg msg) {
SimpleQuery<L2NetworkClusterRefVO> query = dbf.createQuery(L2NetworkClusterRefVO.class);
query.add(L2NetworkClusterRefVO_.clusterUuid, Op.EQ, msg.getClusterUuid());
query.add(L2NetworkClusterRefVO_.l2NetworkUuid, Op.EQ, msg.getL2NetworkUuid());
L2NetworkClusterRefVO rvo = query.find();
if (rvo != null) {
dbf.remove(rvo);
}
DetachL2NetworkFromClusterReply reply = new DetachL2NetworkFromClusterReply();
bus.reply(msg, reply);
}
private void handle(final PrepareL2NetworkOnHostMsg msg) {
final PrepareL2NetworkOnHostReply reply = new PrepareL2NetworkOnHostReply();
prepareL2NetworkOnHosts(Arrays.asList(msg.getHost()), 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 CheckL2NetworkOnHostMsg msg) {
SimpleQuery<HostVO> q = dbf.createQuery(HostVO.class);
q.select(HostVO_.hypervisorType);
q.add(HostVO_.uuid, Op.EQ, msg.getHostUuid());
String htype = q.findValue();
final HypervisorType hvType = HypervisorType.valueOf(htype);
final L2NetworkType l2Type = L2NetworkType.valueOf(self.getType());
final CheckL2NetworkOnHostReply reply = new CheckL2NetworkOnHostReply();
L2NetworkRealizationExtensionPoint ext = l2Mgr.getRealizationExtension(l2Type, hvType);
ext.check(getSelfInventory(), msg.getHostUuid(), 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(L2NetworkDeletionMsg msg) {
L2NetworkInventory inv = L2NetworkInventory.valueOf(self);
extpEmitter.beforeDelete(inv);
deleteHook();
extpEmitter.afterDelete(inv);
L2NetworkDeletionReply reply = new L2NetworkDeletionReply();
bus.reply(msg, reply);
}
private void handleApiMessage(APIMessage msg) {
if (msg instanceof APIDeleteL2NetworkMsg) {
handle((APIDeleteL2NetworkMsg) msg);
} else if (msg instanceof APIAttachL2NetworkToClusterMsg) {
handle((APIAttachL2NetworkToClusterMsg) msg);
} else if (msg instanceof APIDetachL2NetworkFromClusterMsg) {
handle((APIDetachL2NetworkFromClusterMsg) msg);
} else if (msg instanceof APIUpdateL2NetworkMsg) {
handle((APIUpdateL2NetworkMsg) msg);
} else {
bus.dealWithUnknownMessage(msg);
}
}
private void handle(APIUpdateL2NetworkMsg msg) {
boolean update = false;
if (msg.getName() != null) {
self.setName(msg.getName());
update = true;
}
if (msg.getDescription() != null) {
self.setDescription(msg.getDescription());
update = true;
}
if (update) {
self = dbf.updateAndRefresh(self);
}
APIUpdateL2NetworkEvent evt = new APIUpdateL2NetworkEvent(msg.getId());
evt.setInventory(getSelfInventory());
bus.publish(evt);
}
private void handle(final APIDetachL2NetworkFromClusterMsg msg) {
final APIDetachL2NetworkFromClusterEvent evt = new APIDetachL2NetworkFromClusterEvent(msg.getId());
String issuer = L2NetworkVO.class.getSimpleName();
List<L2NetworkDetachStruct> ctx = new ArrayList<L2NetworkDetachStruct>();
L2NetworkDetachStruct struct = new L2NetworkDetachStruct();
struct.setClusterUuid(msg.getClusterUuid());
struct.setL2NetworkUuid(msg.getL2NetworkUuid());
ctx.add(struct);
casf.asyncCascade(L2NetworkConstant.DETACH_L2NETWORK_CODE, issuer, ctx, new Completion(msg) {
@Override
public void success() {
logger.debug(String.format("successfully detached L2Network[uuid:%s] to cluster [uuid:%s]", self.getUuid(), msg.getClusterUuid()));
self = dbf.reload(self);
evt.setInventory((L2NetworkInventory) inventoryMgr.valueOf(self));
bus.publish(evt);
}
@Override
public void fail(ErrorCode errorCode) {
evt.setError(errorCode);
bus.publish(evt);
}
});
}
protected void realizeNetwork(String hostUuid, String htype, Completion completion) {
final HypervisorType hvType = HypervisorType.valueOf(htype);
final L2NetworkType l2Type = L2NetworkType.valueOf(self.getType());
L2NetworkRealizationExtensionPoint ext = l2Mgr.getRealizationExtension(l2Type, hvType);
ext.realize(getSelfInventory(), hostUuid, completion);
}
private void prepareL2NetworkOnHosts(final List<HostInventory> hosts, final Completion completion) {
FlowChain chain = FlowChainBuilder.newSimpleFlowChain();
chain.setName(String.format("prepare-l2-%s-on-hosts", self.getUuid()));
chain.then(new NoRollbackFlow() {
@Override
public void run(final FlowTrigger trigger, Map data) {
List<CheckNetworkPhysicalInterfaceMsg> cmsgs = new ArrayList<CheckNetworkPhysicalInterfaceMsg>();
for (HostInventory h : hosts) {
CheckNetworkPhysicalInterfaceMsg cmsg = new CheckNetworkPhysicalInterfaceMsg();
cmsg.setHostUuid(h.getUuid());
cmsg.setPhysicalInterface(self.getPhysicalInterface());
bus.makeTargetServiceIdByResourceUuid(cmsg, HostConstant.SERVICE_ID, h.getUuid());
cmsgs.add(cmsg);
}
if (cmsgs.isEmpty()) {
trigger.next();
return;
}
bus.send(cmsgs, new CloudBusListCallBack(trigger) {
@Override
public void run(List<MessageReply> replies) {
for (MessageReply r : replies) {
if (!r.isSuccess()) {
trigger.fail(r.getError());
return;
}
}
trigger.next();
}
});
}
}).then(new NoRollbackFlow() {
private void realize(final Iterator<HostInventory> it, final FlowTrigger trigger) {
if (!it.hasNext()) {
trigger.next();
return;
}
HostInventory host = it.next();
realizeNetwork(host.getUuid(), host.getHypervisorType(), new Completion(trigger) {
@Override
public void success() {
realize(it, trigger);
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
@Override
public void run(FlowTrigger trigger, Map data) {
realize(hosts.iterator(), trigger);
}
}).done(new FlowDoneHandler(completion) {
@Override
public void handle(Map data) {
completion.success();
}
}).error(new FlowErrorHandler(completion) {
@Override
public void handle(ErrorCode errCode, Map data) {
completion.fail(errCode);
}
}).start();
}
private void handle(final APIAttachL2NetworkToClusterMsg msg) {
thdf.chainSubmit(new ChainTask(msg) {
@Override
public String getSyncSignature() {
return String.format("attach-l2-network-to-cluster-%s", msg.getClusterUuid());
}
@Override
public void run(SyncTaskChain chain) {
attachL2NetworkToCluster(msg, new NoErrorCompletion(msg,chain) {
@Override
public void done() {
chain.next();
}
});
}
@Override
public String getName() {
return getSyncSignature();
}
});
}
private void handle(APIDeleteL2NetworkMsg msg) {
final APIDeleteL2NetworkEvent evt = new APIDeleteL2NetworkEvent(msg.getId());
final String issuer = L2NetworkVO.class.getSimpleName();
final List<L2NetworkInventory> ctx = L2NetworkInventory.valueOf(Arrays.asList(self));
FlowChain chain = FlowChainBuilder.newSimpleFlowChain();
chain.setName(String.format("delete-l2Network-%s", msg.getL2NetworkUuid()));
if (msg.getDeletionMode() == APIDeleteMessage.DeletionMode.Permissive) {
chain.then(new NoRollbackFlow() {
@Override
public void run(final FlowTrigger trigger, Map data) {
casf.asyncCascade(CascadeConstant.DELETION_CHECK_CODE, issuer, ctx, new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
}).then(new NoRollbackFlow() {
@Override
public void run(final FlowTrigger trigger, Map data) {
casf.asyncCascade(CascadeConstant.DELETION_DELETE_CODE, issuer, ctx, new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
});
} else {
chain.then(new NoRollbackFlow() {
@Override
public void run(final FlowTrigger trigger, Map data) {
casf.asyncCascade(CascadeConstant.DELETION_FORCE_DELETE_CODE, issuer, ctx, new Completion(trigger) {
@Override
public void success() {
trigger.next();
}
@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
});
}
chain.done(new FlowDoneHandler(msg) {
@Override
public void handle(Map data) {
casf.asyncCascadeFull(CascadeConstant.DELETION_CLEANUP_CODE, issuer, ctx, new NopeCompletion());
bus.publish(evt);
}
}).error(new FlowErrorHandler(msg) {
@Override
public void handle(ErrorCode errCode, Map data) {
evt.setError(errf.instantiateErrorCode(SysErrors.DELETE_RESOURCE_ERROR, errCode));
bus.publish(evt);
}
}).start();
}
private void attachL2NetworkToCluster(final APIAttachL2NetworkToClusterMsg msg, final NoErrorCompletion completion){
final APIAttachL2NetworkToClusterEvent evt = new APIAttachL2NetworkToClusterEvent(msg.getId());
long count = Q.New(L2NetworkClusterRefVO.class).eq(L2NetworkClusterRefVO_.clusterUuid, msg.getClusterUuid())
.eq(L2NetworkClusterRefVO_.l2NetworkUuid, msg.getL2NetworkUuid()).count();
if (count != 0) {
evt.setInventory((L2NetworkInventory) inventoryMgr.valueOf(self));
bus.publish(evt);
completion.done();
return;
}
new SQLBatch() {
@Override
protected void scripts() {
String type = Q.New(L2NetworkVO.class).select(L2NetworkVO_.type).eq(L2NetworkVO_.uuid, msg.getL2NetworkUuid()).findValue();
if (L2NetworkConstant.L2_NO_VLAN_NETWORK_TYPE.equals(type)) {
List<L2NetworkVO> l2s = SQL.New("select l2" +
" from L2NetworkVO l2, L2NetworkClusterRefVO ref" +
" where l2.uuid = ref.l2NetworkUuid" +
" and ref.clusterUuid = :clusterUuid" +
" and type = 'L2NoVlanNetwork'")
.param("clusterUuid", msg.getClusterUuid()).list();
if (l2s.isEmpty()) {
return;
}
L2NetworkVO tl2 = Q.New(L2NetworkVO.class).eq(L2NetworkVO_.uuid, msg.getL2NetworkUuid()).find();
for (L2NetworkVO l2 : l2s) {
if (l2.getPhysicalInterface().equals(tl2.getPhysicalInterface())) {
throw new ApiMessageInterceptionException(argerr("There has been a l2Network[uuid:%s, name:%s] attached to cluster[uuid:%s] that has physical interface[%s]. Failed to attach l2Network[uuid:%s]",
l2.getUuid(), l2.getName(), msg.getClusterUuid(), l2.getPhysicalInterface(), tl2.getUuid()));
}
}
} else if (L2NetworkConstant.L2_VLAN_NETWORK_TYPE.equals(type)) {
List<L2VlanNetworkVO> l2s = SQL.New("select l2" +
" from L2VlanNetworkVO l2, L2NetworkClusterRefVO ref" +
" where l2.uuid = ref.l2NetworkUuid" +
" and ref.clusterUuid = :clusterUuid")
.param("clusterUuid", msg.getClusterUuid()).list();
if (l2s.isEmpty()) {
return;
}
L2VlanNetworkVO tl2 = Q.New(L2VlanNetworkVO.class).eq(L2VlanNetworkVO_.uuid, msg.getL2NetworkUuid()).find();
for (L2VlanNetworkVO vl2 : l2s) {
if (vl2.getVlan() == tl2.getVlan() && vl2.getPhysicalInterface().equals(tl2.getPhysicalInterface())) {
throw new OperationFailureException(argerr("There has been a L2VlanNetwork[uuid:%s, name:%s] attached to cluster[uuid:%s] that has physical interface[%s], vlan[%s]. Failed to attach L2VlanNetwork[uuid:%s]",
vl2.getUuid(), vl2.getName(), msg.getClusterUuid(), vl2.getPhysicalInterface(), vl2.getVlan(), tl2.getUuid()));
}
}
}
}
}.execute();
List<HostVO> hosts = Q.New(HostVO.class).eq(HostVO_.clusterUuid,msg.getClusterUuid())
.notIn(HostVO_.state,asList(HostState.PreMaintenance, HostState.Maintenance))
.eq(HostVO_.status,HostStatus.Connected).list();
List<HostInventory> hvinvs = HostInventory.valueOf(hosts);
prepareL2NetworkOnHosts(hvinvs, new Completion(msg,completion) {
@Override
public void success() {
L2NetworkClusterRefVO rvo = new L2NetworkClusterRefVO();
rvo.setClusterUuid(msg.getClusterUuid());
rvo.setL2NetworkUuid(self.getUuid());
dbf.persist(rvo);
logger.debug(String.format("successfully attached L2Network[uuid:%s] to cluster [uuid:%s]", self.getUuid(), msg.getClusterUuid()));
self = dbf.findByUuid(self.getUuid(), L2NetworkVO.class);
evt.setInventory((L2NetworkInventory) inventoryMgr.valueOf(self));
bus.publish(evt);
completion.done();
}
@Override
public void fail(ErrorCode errorCode) {
evt.setError(errf.instantiateErrorCode(L2Errors.ATTACH_ERROR, errorCode));
bus.publish(evt);
completion.done();
}
});
}
}