package org.zstack.mediator;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.SimpleQuery;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.header.apimediator.ApiMessageInterceptionException;
import org.zstack.header.apimediator.GlobalApiMessageInterceptor;
import org.zstack.header.message.APIMessage;
import org.zstack.header.vm.VmInstanceVO;
import org.zstack.network.service.eip.APIAttachEipMsg;
import org.zstack.network.service.eip.APICreateEipMsg;
import org.zstack.network.service.eip.EipVO;
import org.zstack.network.service.eip.EipVO_;
import org.zstack.network.service.portforwarding.APIAttachPortForwardingRuleMsg;
import org.zstack.network.service.portforwarding.APICreatePortForwardingRuleMsg;
import org.zstack.network.service.portforwarding.PortForwardingRuleVO;
import org.zstack.network.service.portforwarding.PortForwardingRuleVO_;
import static org.zstack.core.Platform.operr;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
*/
public class ApiValidator implements GlobalApiMessageInterceptor {
@Autowired
private DatabaseFacade dbf;
@Autowired
private ErrorFacade errf;
@Override
public List<Class> getMessageClassToIntercept() {
List<Class> ret = new ArrayList<>();
ret.add(APICreateEipMsg.class);
ret.add(APIAttachEipMsg.class);
ret.add(APICreatePortForwardingRuleMsg.class);
ret.add(APIAttachPortForwardingRuleMsg.class);
return ret;
}
@Override
public InterceptorPosition getPosition() {
return InterceptorPosition.END;
}
@Override
public APIMessage intercept(APIMessage msg) throws ApiMessageInterceptionException {
if (msg instanceof APICreateEipMsg) {
validate((APICreateEipMsg) msg);
} else if (msg instanceof APIAttachEipMsg) {
validate((APIAttachEipMsg) msg);
} else if (msg instanceof APICreatePortForwardingRuleMsg) {
validate((APICreatePortForwardingRuleMsg) msg);
} else if (msg instanceof APIAttachPortForwardingRuleMsg) {
validate((APIAttachPortForwardingRuleMsg) msg);
}
return msg;
}
private void validate(APIAttachPortForwardingRuleMsg msg) {
isVmNicUsedByEip(msg.getVmNicUuid());
}
private void validate(APICreatePortForwardingRuleMsg msg) {
if (msg.getVmNicUuid() != null) {
isVmNicUsedByEip(msg.getVmNicUuid());
}
}
@Transactional(readOnly = true)
private void isVmNicUsedByPortForwarding(String vmNicUuid) {
String sql = "select pf from PortForwardingRuleVO pf, VmInstanceVO vm, VmNicVO nic where pf.vmNicUuid = nic.uuid" +
" and nic.vmInstanceUuid = vm.uuid and vm.uuid = (select n.vmInstanceUuid from VmNicVO n where n.uuid = :nicUuid)";
TypedQuery<PortForwardingRuleVO> q = dbf.getEntityManager().createQuery(sql, PortForwardingRuleVO.class);
q.setParameter("nicUuid", vmNicUuid);
List<PortForwardingRuleVO> pfs = q.getResultList();
if (!pfs.isEmpty()) {
sql = "select vm from VmInstanceVO vm, VmNicVO nic where vm.uuid = nic.vmInstanceUuid and nic.uuid = :nicUuid";
TypedQuery<VmInstanceVO> vq = dbf.getEntityManager().createQuery(sql, VmInstanceVO.class);
vq.setParameter("nicUuid", vmNicUuid);
VmInstanceVO vm = vq.getSingleResult();
List<String> pfStr = pfs.stream().map(pf -> String.format("(name:%s, ip:%s)", pf.getName(), pf.getVipIp())).collect(Collectors.toList());
throw new ApiMessageInterceptionException(operr("the vm[name:%s, uuid:%s] already has some port forwarding rules%s attached", vm.getName(), vm.getUuid(),
StringUtils.join(pfStr, ",")));
}
}
@Transactional(readOnly = true)
private void isVmNicUsedByEip(String vmNicUuid) {
String sql = "select eip from EipVO eip, VmInstanceVO vm, VmNicVO nic where eip.vmNicUuid = nic.uuid" +
" and nic.vmInstanceUuid = vm.uuid and vm.uuid = (select n.vmInstanceUuid from VmNicVO n where n.uuid = :nicUuid)";
TypedQuery<EipVO> q = dbf.getEntityManager().createQuery(sql, EipVO.class);
q.setParameter("nicUuid", vmNicUuid);
List<EipVO> eips = q.getResultList();
if (!eips.isEmpty()) {
sql = "select vm from VmInstanceVO vm, VmNicVO nic where vm.uuid = nic.vmInstanceUuid and nic.uuid = :nicUuid";
TypedQuery<VmInstanceVO> vq = dbf.getEntityManager().createQuery(sql, VmInstanceVO.class);
vq.setParameter("nicUuid", vmNicUuid);
VmInstanceVO vm = vq.getSingleResult();
List<String> eipStr = eips.stream().map(eip -> String.format("(name:%s, ip:%s)", eip.getName(), eip.getVipIp())).collect(Collectors.toList());
throw new ApiMessageInterceptionException(operr("the vm[name:%s, uuid:%s] already has some EIPs%s attached", vm.getName(), vm.getUuid(),
StringUtils.join(eipStr, ",")));
}
}
private void validate(APIAttachEipMsg msg) {
isVmNicUsedByPortForwarding(msg.getVmNicUuid());
}
private void validate(APICreateEipMsg msg) {
if (msg.getVmNicUuid() != null) {
isVmNicUsedByPortForwarding(msg.getVmNicUuid());
}
}
}