package org.zstack.network.securitygroup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.Platform;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.MessageSafe;
import org.zstack.core.componentloader.PluginRegistry;
import org.zstack.core.config.GlobalConfig;
import org.zstack.core.config.GlobalConfigUpdateExtensionPoint;
import org.zstack.core.db.*;
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.core.thread.AsyncThread;
import org.zstack.core.thread.PeriodicTask;
import org.zstack.core.thread.ThreadFacade;
import org.zstack.header.AbstractService;
import org.zstack.header.apimediator.ApiMessageInterceptionException;
import org.zstack.header.core.Completion;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.host.HostStatus;
import org.zstack.header.identity.*;
import org.zstack.header.identity.Quota.QuotaOperator;
import org.zstack.header.identity.Quota.QuotaPair;
import org.zstack.header.managementnode.ManagementNodeReadyExtensionPoint;
import org.zstack.header.message.APIMessage;
import org.zstack.header.message.Message;
import org.zstack.header.message.NeedQuotaCheckMessage;
import org.zstack.header.query.AddExpandedQueryExtensionPoint;
import org.zstack.header.query.ExpandedQueryAliasStruct;
import org.zstack.header.query.ExpandedQueryStruct;
import org.zstack.header.vm.*;
import org.zstack.identity.AccountManager;
import org.zstack.identity.QuotaUtil;
import org.zstack.network.securitygroup.APIAddSecurityGroupRuleMsg.SecurityGroupRuleAO;
import org.zstack.query.QueryFacade;
import org.zstack.tag.TagManager;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.data.Pair;
import org.zstack.utils.function.Function;
import org.zstack.utils.gson.JSONObjectUtil;
import org.zstack.utils.logging.CLogger;
import org.zstack.utils.network.NetworkUtils;
import javax.persistence.LockModeType;
import javax.persistence.Query;
import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static java.util.Arrays.asList;
import static org.zstack.utils.CollectionDSL.list;
public class SecurityGroupManagerImpl extends AbstractService implements SecurityGroupManager, ManagementNodeReadyExtensionPoint,
VmInstanceMigrateExtensionPoint, AddExpandedQueryExtensionPoint, ReportQuotaExtensionPoint {
private static CLogger logger = Utils.getLogger(SecurityGroupManagerImpl.class);
@Autowired
private CloudBus bus;
@Autowired
private DatabaseFacade dbf;
@Autowired
private DbEntityLister dl;
@Autowired
private PluginRegistry pluginRgty;
@Autowired
private ThreadFacade thdf;
@Autowired
private QueryFacade qf;
@Autowired
private AccountManager acntMgr;
@Autowired
private TagManager tagMgr;
@Autowired
private ErrorFacade errf;
protected Map<String, SecurityGroupHypervisorBackend> hypervisorBackends;
private int failureHostWorkerInterval;
private int failureHostEachTimeTake;
private Future<Void> failureHostCopingThread;
@Override
public List<Quota> reportQuota() {
QuotaOperator checker = new QuotaOperator() {
@Override
public void checkQuota(APIMessage msg, Map<String, QuotaPair> pairs) {
if (!new QuotaUtil().isAdminAccount(msg.getSession().getAccountUuid())) {
if (msg instanceof APICreateSecurityGroupMsg) {
check((APICreateSecurityGroupMsg) msg, pairs);
}
}
}
@Override
public void checkQuota(NeedQuotaCheckMessage msg, Map<String, QuotaPair> pairs) {
}
@Override
public List<Quota.QuotaUsage> getQuotaUsageByAccount(String accountUuid) {
Quota.QuotaUsage usage = new Quota.QuotaUsage();
usage.setName(SecurityGroupConstant.QUOTA_SG_NUM);
usage.setUsed(getUsedSg(accountUuid));
return list(usage);
}
@Transactional(readOnly = true)
private long getUsedSg(String accountUuid) {
String sql = "select count(sg) from SecurityGroupVO sg, AccountResourceRefVO ref where ref.resourceUuid = sg.uuid" +
" and ref.accountUuid = :auuid and ref.resourceType = :rtype";
TypedQuery<Long> q = dbf.getEntityManager().createQuery(sql, Long.class);
q.setParameter("auuid", accountUuid);
q.setParameter("rtype", SecurityGroupVO.class.getSimpleName());
Long sgn = q.getSingleResult();
sgn = sgn == null ? 0 : sgn;
return sgn;
}
private void check(APICreateSecurityGroupMsg msg, Map<String, QuotaPair> pairs) {
long sgNum = pairs.get(SecurityGroupConstant.QUOTA_SG_NUM).getValue();
long sgn = getUsedSg(msg.getSession().getAccountUuid());
if (sgn + 1 > sgNum) {
throw new ApiMessageInterceptionException(errf.instantiateErrorCode(IdentityErrors.QUOTA_EXCEEDING,
String.format("quota exceeding. The account[uuid: %s] exceeds a quota[name: %s, value: %s]",
msg.getSession().getAccountUuid(), SecurityGroupConstant.QUOTA_SG_NUM, sgNum)
));
}
}
};
Quota quota = new Quota();
quota.setOperator(checker);
quota.addMessageNeedValidation(APICreateSecurityGroupMsg.class);
QuotaPair p = new QuotaPair();
p.setName(SecurityGroupConstant.QUOTA_SG_NUM);
p.setValue(20);
quota.addPair(p);
return list(quota);
}
@Override
@AsyncThread
public void managementNodeReady() {
startFailureHostCopingThread();
}
private class RuleCalculator {
private List<String> vmNicUuids;
private List<String> l3NetworkUuids;
private List<String> securityGroupUuids;
private List<String> hostUuids;
private List<VmInstanceState> vmStates;
List<HostRuleTO> calculate() {
if (vmNicUuids != null) {
return calculateByVmNic();
} else if (l3NetworkUuids != null && securityGroupUuids != null) {
return calculateByL3NetworkAndSecurityGroup();
} else if (l3NetworkUuids != null) {
return calculateByL3Network();
} else if (securityGroupUuids != null) {
return calculateBySecurityGroup();
} else if (hostUuids != null) {
return calculateByHost();
}
throw new CloudRuntimeException("should not be here");
}
@Transactional(readOnly = true)
private List<HostRuleTO> calculateByHost() {
String sql = "select nic.uuid from VmNicVO nic, VmInstanceVO vm, VmNicSecurityGroupRefVO ref" +
" where nic.uuid = ref.vmNicUuid and nic.vmInstanceUuid = vm.uuid" +
" and vm.hostUuid in (:hostUuids) and vm.state in (:vmStates)";
TypedQuery<String> insgQuery = dbf.getEntityManager().createQuery(sql, String.class);
insgQuery.setParameter("hostUuids", hostUuids);
insgQuery.setParameter("vmStates", vmStates);
List<String> nicsInSg = insgQuery.getResultList();
sql = "select nic.uuid from VmNicVO nic, VmInstanceVO vm where nic.vmInstanceUuid = vm.uuid" +
" and vm.hostUuid in (:hostUuids) and vm.state in (:vmStates)";
TypedQuery<String> allq = dbf.getEntityManager().createQuery(sql, String.class);
allq.setParameter("hostUuids", hostUuids);
allq.setParameter("vmStates", vmStates);
List<String> allNics = allq.getResultList();
allNics.removeAll(nicsInSg);
List<String> nicsOutSg = allNics;
List<HostRuleTO> ret = new ArrayList<HostRuleTO>();
if (!nicsInSg.isEmpty()) {
vmNicUuids = nicsInSg;
ret.addAll(calculateByVmNic());
}
if (!nicsOutSg.isEmpty()) {
Collection<HostRuleTO> toRemove = createRulePlaceHolder(nicsOutSg);
for (HostRuleTO hto : toRemove) {
hto.setActionCodeForAllSecurityGroupRuleTOs(SecurityGroupRuleTO.ACTION_CODE_DELETE_CHAIN);
}
ret.addAll(toRemove);
}
return ret;
}
@Transactional(readOnly = true)
private List<HostRuleTO> calculateBySecurityGroup() {
String sql = "select ref.vmNicUuid from VmNicSecurityGroupRefVO ref where ref.securityGroupUuid in (:sgUuids)";
TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
q.setParameter("sgUuids", securityGroupUuids);
vmNicUuids = q.getResultList();
return calculateByVmNic();
}
private List<HostRuleTO> calculateByL3Network() {
return null;
}
List<HostRuleTO> mergeMultiHostRuleTO(Collection<HostRuleTO>... htos) {
Map<String, HostRuleTO> hostRuleTOMap = new HashMap<String, HostRuleTO>();
for (Collection<HostRuleTO> lst : htos) {
for (HostRuleTO hto : lst) {
HostRuleTO old = hostRuleTOMap.get(hto.getHostUuid());
if (old == null) {
hostRuleTOMap.put(hto.getHostUuid(), hto);
} else {
old.getRules().addAll(hto.getRules());
}
}
}
List<HostRuleTO> ret = new ArrayList<HostRuleTO>(hostRuleTOMap.size());
ret.addAll(hostRuleTOMap.values());
return ret;
}
private List<HostRuleTO> calculateByL3NetworkAndSecurityGroup() {
String sql = "select ref.vmNicUuid from VmNicSecurityGroupRefVO ref, SecurityGroupL3NetworkRefVO l3ref, VmNicVO nic where l3ref.securityGroupUuid = ref.securityGroupUuid and nic.uuid = ref.vmNicUuid and nic.l3NetworkUuid = l3ref.l3NetworkUuid and ref.securityGroupUuid in (:sgUuids) and l3ref.l3NetworkUuid in (:l3Uuids)";
TypedQuery<String> q = dbf.getEntityManager().createQuery(sql, String.class);
q.setParameter("sgUuids", securityGroupUuids);
q.setParameter("l3Uuids", l3NetworkUuids);
vmNicUuids = q.getResultList();
return calculateByVmNic();
}
private List<RuleTO> calculateRuleTOBySecurityGroup(List<String> sgUuids, String l3Uuid) {
List<RuleTO> ret = new ArrayList<RuleTO>();
for (String sgUuid : sgUuids) {
String sql = "select r from SecurityGroupRuleVO r where r.securityGroupUuid = :sgUuid";
TypedQuery<SecurityGroupRuleVO> q = dbf.getEntityManager().createQuery(sql, SecurityGroupRuleVO.class);
q.setParameter("sgUuid", sgUuid);
List<SecurityGroupRuleVO> rules = q.getResultList();
if (rules.isEmpty()) {
continue;
}
sql = "select nic.ip from VmNicVO nic, VmNicSecurityGroupRefVO ref where ref.vmNicUuid = nic.uuid and ref.securityGroupUuid = :sgUuid and nic.l3NetworkUuid = :l3Uuid";
TypedQuery<String> internalIpQuery = dbf.getEntityManager().createQuery(sql, String.class);
internalIpQuery.setParameter("sgUuid", sgUuid);
internalIpQuery.setParameter("l3Uuid", l3Uuid);
List<String> internalIps = internalIpQuery.getResultList();
List<Pair<String, String>> ipRanges = NetworkUtils.findConsecutiveIpRange(internalIps);
List<String> internalIpRanges = new ArrayList<String>(ipRanges.size());
for (Pair<String, String> p : ipRanges) {
if (p.first().equals(p.second())) {
internalIpRanges.add(p.first());
} else {
internalIpRanges.add(String.format("%s-%s", p.first(), p.second()));
}
}
for (SecurityGroupRuleVO r : rules) {
RuleTO rto = new RuleTO();
rto.setAllowedCidr(r.getAllowedCidr());
rto.setEndPort(r.getEndPort());
rto.setProtocol(r.getProtocol().toString());
rto.setStartPort(r.getStartPort());
rto.setType(r.getType().toString());
rto.setAllowedInternalIpRange(internalIpRanges);
ret.add(rto);
}
}
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder();
sb.append(String.format("\n-------------- begin calculateRuleTOBySecurityGroupUuid ---------------------"));
sb.append(String.format("\ninput security group uuids: %s", sgUuids));
sb.append(String.format("\nresult: %s", JSONObjectUtil.toJsonString(ret)));
sb.append(String.format("\n-------------- end calculateRuleTOBySecurityGroupUuid ---------------------"));
logger.trace(sb.toString());
}
return ret;
}
@Transactional(readOnly = true)
Collection<HostRuleTO> createRulePlaceHolder(List<String> nicUuids) {
String sql = "select nic.uuid, vm.hostUuid, vm.hypervisorType, nic.internalName, nic.mac, nic.ip from VmInstanceVO vm, VmNicVO nic where nic.vmInstanceUuid = vm.uuid and vm.hostUuid is not null and nic.uuid in (:nicUuids) group by nic.uuid";
TypedQuery<Tuple> q = dbf.getEntityManager().createQuery(sql, Tuple.class);
q.setParameter("nicUuids", nicUuids);
List<Tuple> tuples = q.getResultList();
sql = "select nic.uuid, vm.lastHostUuid, vm.hypervisorType, nic.internalName, nic.mac, nic.ip from VmInstanceVO vm, VmNicVO nic where nic.vmInstanceUuid = vm.uuid and vm.hostUuid is null and vm.lastHostUuid is not null and nic.uuid in (:nicUuids) group by nic.uuid";
q = dbf.getEntityManager().createQuery(sql, Tuple.class);
q.setParameter("nicUuids", nicUuids);
tuples.addAll(q.getResultList());
Map<String, HostRuleTO> hostRuleTOMap = new HashMap<String, HostRuleTO>();
for (Tuple t : tuples) {
String nicUuid = t.get(0, String.class);
String hostUuid = t.get(1, String.class);
String hvType = t.get(2, String.class);
String nicName = t.get(3, String.class);
String mac = t.get(4, String.class);
String ip = t.get(5, String.class);
SecurityGroupRuleTO sgto = new SecurityGroupRuleTO();
sgto.setEgressDefaultPolicy(SecurityGroupGlobalConfig.EGRESS_RULE_DEFAULT_POLICY.value(String.class));
sgto.setIngressDefaultPolicy(SecurityGroupGlobalConfig.INGRESS_RULE_DEFAULT_POLICY.value(String.class));
sgto.setRules(new ArrayList<RuleTO>());
sgto.setVmNicUuid(nicUuid);
sgto.setVmNicInternalName(nicName);
sgto.setVmNicMac(mac);
sgto.setVmNicIp(ip);
HostRuleTO hto = hostRuleTOMap.get(hostUuid);
if (hto == null) {
hto = new HostRuleTO();
hto.setHostUuid(hostUuid);
hto.setHypervisorType(hvType);
hostRuleTOMap.put(hto.getHostUuid(), hto);
}
hto.getRules().add(sgto);
}
return hostRuleTOMap.values();
}
@Transactional(readOnly = true)
private List<HostRuleTO> calculateByVmNic() {
Map<String, HostRuleTO> hostRuleMap = new HashMap<String, HostRuleTO>();
List<HostRuleTO> htos = new ArrayList<HostRuleTO>();
for (String nicUuid : vmNicUuids) {
List<Tuple> tuples;
if (vmStates != null && !vmStates.isEmpty()) {
String sql = "select ref.securityGroupUuid, vm.hostUuid, vm.hypervisorType, nic.internalName, nic.l3NetworkUuid, nic.mac, nic.ip from VmNicSecurityGroupRefVO ref, VmInstanceVO vm, VmNicVO nic where ref.vmNicUuid = nic.uuid and nic.vmInstanceUuid = vm.uuid and ref.vmNicUuid = :nicUuid and vm.state in (:vmStates)";
TypedQuery<Tuple> q = dbf.getEntityManager().createQuery(sql, Tuple.class);
q.setParameter("nicUuid", nicUuid);
q.setParameter("vmStates", vmStates);
tuples = q.getResultList();
} else {
String sql = "select ref.securityGroupUuid, vm.hostUuid, vm.hypervisorType, nic.internalName, nic.l3NetworkUuid, nic.mac, nic.ip from VmNicSecurityGroupRefVO ref, VmInstanceVO vm, VmNicVO nic where ref.vmNicUuid = nic.uuid and nic.vmInstanceUuid = vm.uuid and ref.vmNicUuid = :nicUuid";
TypedQuery<Tuple> q = dbf.getEntityManager().createQuery(sql, Tuple.class);
q.setParameter("nicUuid", nicUuid);
tuples = q.getResultList();
}
if (tuples.isEmpty()) {
// vm is not in vmStates or not in security group
continue;
}
List<String> sgUuids = new ArrayList<String>();
String hostUuid = null;
String hypervisorType = null;
String nicName = null;
String l3Uuid = null;
String mac = null;
String ip = null;
for (Tuple t : tuples) {
sgUuids.add(t.get(0, String.class));
hostUuid = t.get(1, String.class);
hypervisorType = t.get(2, String.class);
nicName = t.get(3, String.class);
l3Uuid = t.get(4, String.class);
mac = t.get(5, String.class);
ip = t.get(6, String.class);
}
List<RuleTO> rtos = calculateRuleTOBySecurityGroup(sgUuids, l3Uuid);
SecurityGroupRuleTO sgto = new SecurityGroupRuleTO();
sgto.setEgressDefaultPolicy(SecurityGroupGlobalConfig.EGRESS_RULE_DEFAULT_POLICY.value(String.class));
sgto.setIngressDefaultPolicy(SecurityGroupGlobalConfig.INGRESS_RULE_DEFAULT_POLICY.value(String.class));
sgto.setRules(rtos);
sgto.setVmNicUuid(nicUuid);
sgto.setVmNicInternalName(nicName);
sgto.setVmNicMac(mac);
sgto.setVmNicIp(ip);
HostRuleTO hto = hostRuleMap.get(hostUuid);
if (hto == null) {
hto = new HostRuleTO();
hto.setHostUuid(hostUuid);
hto.setHypervisorType(hypervisorType);
hostRuleMap.put(hto.getHostUuid(), hto);
}
hto.getRules().add(sgto);
}
htos.addAll(hostRuleMap.values());
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder();
sb.append(String.format("\n=================== begin rulesByNicUuids ======================"));
sb.append(String.format("\ninput vmNic uuids: %s", vmNicUuids));
sb.append(String.format("\nresult: %s", JSONObjectUtil.toJsonString(htos)));
sb.append(String.format("\n=================== end rulesByNicUuids ========================"));
logger.trace(sb.toString());
}
return htos;
}
}
@MessageSafe
@Override
public void handleMessage(Message msg) {
if (msg instanceof APIMessage) {
handleApiMessage((APIMessage) msg);
} else {
handleLocalMessage(msg);
}
}
private void handleLocalMessage(Message msg) {
if (msg instanceof RefreshSecurityGroupRulesOnHostMsg) {
handle((RefreshSecurityGroupRulesOnHostMsg) msg);
} else if (msg instanceof RefreshSecurityGroupRulesOnVmMsg) {
handle((RefreshSecurityGroupRulesOnVmMsg) msg);
} else if (msg instanceof RemoveVmNicFromSecurityGroupMsg) {
handle((RemoveVmNicFromSecurityGroupMsg) msg);
} else {
bus.dealWithUnknownMessage(msg);
}
}
private void handle(RemoveVmNicFromSecurityGroupMsg msg) {
RemoveVmNicFromSecurityGroupReply reply = new RemoveVmNicFromSecurityGroupReply();
removeNicFromSecurityGroup(msg.getSecurityGroupUuid(), msg.getVmNicUuids());
bus.reply(msg, reply);
}
private void handle(RefreshSecurityGroupRulesOnVmMsg msg) {
RefreshSecurityGroupRulesOnVmReply reply = new RefreshSecurityGroupRulesOnVmReply();
SimpleQuery<VmNicSecurityGroupRefVO> q = dbf.createQuery(VmNicSecurityGroupRefVO.class);
q.select(VmNicSecurityGroupRefVO_.vmNicUuid);
q.add(VmNicSecurityGroupRefVO_.vmInstanceUuid, Op.EQ, msg.getVmInstanceUuid());
List<String> nicUuids = q.listValue();
if (nicUuids.isEmpty()) {
logger.debug(String.format("no nic of vm[uuid:%s] needs to refresh security group rule", msg.getVmInstanceUuid()));
bus.reply(msg, reply);
return;
}
Collection<HostRuleTO> htos;
if (msg.isDeleteAllRules()) {
RuleCalculator cal = new RuleCalculator();
htos = cal.createRulePlaceHolder(nicUuids);
for (HostRuleTO hto : htos) {
hto.setActionCodeForAllSecurityGroupRuleTOs(SecurityGroupRuleTO.ACTION_CODE_DELETE_CHAIN);
}
} else {
RuleCalculator cal = new RuleCalculator();
cal.vmNicUuids = nicUuids;
htos = cal.calculate();
}
for (HostRuleTO hto : htos) {
if (hto.getHostUuid() == null) {
hto.setHostUuid(msg.getHostUuid());
}
}
applyRules(htos);
logger.debug(String.format("refreshed security group rule for vm[uuid:%s]", msg.getVmInstanceUuid()));
bus.reply(msg, reply);
}
private void createFailureHostTask(String huuid) {
SecurityGroupFailureHostVO fvo = new SecurityGroupFailureHostVO();
fvo.setHostUuid(huuid);
dbf.persist(fvo);
}
private void handle(RefreshSecurityGroupRulesOnHostMsg msg) {
RuleCalculator cal = new RuleCalculator();
cal.hostUuids = asList(msg.getHostUuid());
// refreshing may happen when host is reconnecting; at that time VMs' states are Unknown
cal.vmStates = asList(VmInstanceState.Unknown, VmInstanceState.Running);
List<HostRuleTO> htos = cal.calculate();
for (HostRuleTO hto : htos) {
hto.setRefreshHost(true);
}
logger.debug(String.format("required to refresh rules on host[uuid:%s]", msg.getHostUuid()));
applyRules(htos);
}
private void handleApiMessage(APIMessage msg) {
if (msg instanceof APICreateSecurityGroupMsg) {
handle((APICreateSecurityGroupMsg) msg);
} else if (msg instanceof APIListSecurityGroupMsg) {
handle((APIListSecurityGroupMsg) msg);
} else if (msg instanceof APIAddSecurityGroupRuleMsg) {
handle((APIAddSecurityGroupRuleMsg) msg);
} else if (msg instanceof APIAddVmNicToSecurityGroupMsg) {
handle((APIAddVmNicToSecurityGroupMsg) msg);
} else if (msg instanceof APIDeleteSecurityGroupRuleMsg) {
handle((APIDeleteSecurityGroupRuleMsg) msg);
} else if (msg instanceof APIDeleteSecurityGroupMsg) {
handle((APIDeleteSecurityGroupMsg) msg);
} else if (msg instanceof APIDeleteVmNicFromSecurityGroupMsg) {
handle((APIDeleteVmNicFromSecurityGroupMsg) msg);
} else if (msg instanceof APIListVmNicInSecurityGroupMsg) {
handle((APIListVmNicInSecurityGroupMsg) msg);
} else if (msg instanceof APIAttachSecurityGroupToL3NetworkMsg) {
handle((APIAttachSecurityGroupToL3NetworkMsg) msg);
} else if (msg instanceof APIQueryVmNicInSecurityGroupMsg) {
handle((APIQueryVmNicInSecurityGroupMsg) msg);
} else if (msg instanceof APIChangeSecurityGroupStateMsg) {
handle((APIChangeSecurityGroupStateMsg) msg);
} else if (msg instanceof APIDetachSecurityGroupFromL3NetworkMsg) {
handle((APIDetachSecurityGroupFromL3NetworkMsg) msg);
} else if (msg instanceof APIGetCandidateVmNicForSecurityGroupMsg) {
handle((APIGetCandidateVmNicForSecurityGroupMsg) msg);
} else if (msg instanceof APIUpdateSecurityGroupMsg) {
handle((APIUpdateSecurityGroupMsg) msg);
} else {
bus.dealWithUnknownMessage(msg);
}
}
private void handle(APIUpdateSecurityGroupMsg msg) {
boolean update = false;
SecurityGroupVO vo = dbf.findByUuid(msg.getUuid(), SecurityGroupVO.class);
if (msg.getName() != null) {
vo.setName(msg.getName());
update = true;
}
if (msg.getDescription() != null) {
vo.setDescription(msg.getDescription());
update = true;
}
if (update) {
vo = dbf.updateAndRefresh(vo);
}
APIUpdateSecurityGroupEvent evt = new APIUpdateSecurityGroupEvent(msg.getId());
evt.setInventory(SecurityGroupInventory.valueOf(vo));
bus.publish(evt);
}
@Transactional(readOnly = true)
private List<VmNicVO> getCandidateVmNic(String sgId, String accountUuid) {
List<String> nicUuidsToInclude = acntMgr.getResourceUuidsCanAccessByAccount(accountUuid, VmNicVO.class);
if (nicUuidsToInclude != null && nicUuidsToInclude.isEmpty()) {
return new ArrayList<VmNicVO>();
}
String sql = "select ref.vmNicUuid from VmNicSecurityGroupRefVO ref where ref.securityGroupUuid = :sgUuid";
TypedQuery<String> nq = dbf.getEntityManager().createQuery(sql, String.class);
nq.setParameter("sgUuid", sgId);
List<String> nicUuidsToExclued = nq.getResultList();
TypedQuery<VmNicVO> q;
if (nicUuidsToInclude == null) {
// accessed by an admin
if (nicUuidsToExclued.isEmpty()) {
sql = "select nic from VmNicVO nic, VmInstanceVO vm, SecurityGroupVO sg, SecurityGroupL3NetworkRefVO ref " +
"where nic.vmInstanceUuid = vm.uuid and nic.l3NetworkUuid = ref.l3NetworkUuid and ref.securityGroupUuid = sg.uuid " +
" and sg.uuid = :sgUuid and vm.type = :vmType and vm.state in (:vmStates) group by nic.uuid";
q = dbf.getEntityManager().createQuery(sql, VmNicVO.class);
} else {
sql = "select nic from VmNicVO nic, VmInstanceVO vm, SecurityGroupVO sg, SecurityGroupL3NetworkRefVO ref " +
"where nic.vmInstanceUuid = vm.uuid and nic.l3NetworkUuid = ref.l3NetworkUuid and ref.securityGroupUuid = sg.uuid " +
" and sg.uuid = :sgUuid and vm.type = :vmType and vm.state in (:vmStates) and nic.uuid not in (:nicUuids) group by nic.uuid";
q = dbf.getEntityManager().createQuery(sql, VmNicVO.class);
q.setParameter("nicUuids", nicUuidsToExclued);
}
} else {
// accessed by a normal account
if (nicUuidsToExclued.isEmpty()) {
sql = "select nic from VmNicVO nic, VmInstanceVO vm, SecurityGroupVO sg, SecurityGroupL3NetworkRefVO ref " +
"where nic.vmInstanceUuid = vm.uuid and nic.l3NetworkUuid = ref.l3NetworkUuid and ref.securityGroupUuid = sg.uuid " +
" and sg.uuid = :sgUuid and vm.type = :vmType and vm.state in (:vmStates) and nic.uuid in (:iuuids) group by nic.uuid";
q = dbf.getEntityManager().createQuery(sql, VmNicVO.class);
q.setParameter("iuuids", nicUuidsToInclude);
} else {
sql = "select nic from VmNicVO nic, VmInstanceVO vm, SecurityGroupVO sg, SecurityGroupL3NetworkRefVO ref " +
"where nic.vmInstanceUuid = vm.uuid and nic.l3NetworkUuid = ref.l3NetworkUuid and ref.securityGroupUuid = sg.uuid " +
" and sg.uuid = :sgUuid and vm.type = :vmType and vm.state in (:vmStates) and nic.uuid not in (:nicUuids) and nic.uuid in (:iuuids) group by nic.uuid";
q = dbf.getEntityManager().createQuery(sql, VmNicVO.class);
q.setParameter("nicUuids", nicUuidsToExclued);
q.setParameter("iuuids", nicUuidsToInclude);
}
}
q.setParameter("sgUuid", sgId);
q.setParameter("vmType", VmInstanceConstant.USER_VM_TYPE);
q.setParameter("vmStates", list(VmInstanceState.Running, VmInstanceState.Stopped));
return q.getResultList();
}
private void handle(APIGetCandidateVmNicForSecurityGroupMsg msg) {
APIGetCandidateVmNicForSecurityGroupReply reply = new APIGetCandidateVmNicForSecurityGroupReply();
reply.setInventories(VmNicInventory.valueOf(getCandidateVmNic(msg.getSecurityGroupUuid(), msg.getSession().getAccountUuid())));
bus.reply(msg, reply);
}
@Transactional
private void detachSecurityGroupFromL3Network(String sgUuid, String l3Uuid) {
String sql = "select ref.uuid from VmNicSecurityGroupRefVO ref, VmNicVO nic where nic.uuid = ref.vmNicUuid and nic.l3NetworkUuid = :l3Uuid and ref.securityGroupUuid = :sgUuid";
TypedQuery<String> tq = dbf.getEntityManager().createQuery(sql, String.class);
tq.setParameter("l3Uuid", l3Uuid);
tq.setParameter("sgUuid", sgUuid);
List<String> refUuids = tq.getResultList();
if (!refUuids.isEmpty()) {
sql = "delete from VmNicSecurityGroupRefVO ref where ref.uuid in (:uuids)";
Query q = dbf.getEntityManager().createQuery(sql);
q.setParameter("uuids", refUuids);
q.executeUpdate();
}
sql = "delete from SecurityGroupL3NetworkRefVO ref where ref.l3NetworkUuid = :l3Uuid and ref.securityGroupUuid = :sgUuid";
Query q = dbf.getEntityManager().createQuery(sql);
q.setParameter("l3Uuid", l3Uuid);
q.setParameter("sgUuid", sgUuid);
q.executeUpdate();
}
@Transactional(readOnly = true)
private List<String> getVmNicUuidsToRemoveForDetachSecurityGroup(String sgUuid, String l3Uuid) {
String sql = "select nic.uuid from VmNicVO nic, VmNicSecurityGroupRefVO ref where ref.vmNicUuid = nic.uuid and nic.l3NetworkUuid = :l3Uuid and ref.securityGroupUuid = :sgUuid";
TypedQuery<String> tq = dbf.getEntityManager().createQuery(sql, String.class);
tq.setParameter("l3Uuid", l3Uuid);
tq.setParameter("sgUuid", sgUuid);
return tq.getResultList();
}
private void handle(APIDetachSecurityGroupFromL3NetworkMsg msg) {
List<String> vmNicUuids = getVmNicUuidsToRemoveForDetachSecurityGroup(msg.getSecurityGroupUuid(), msg.getL3NetworkUuid());
if (!vmNicUuids.isEmpty()) {
removeNicFromSecurityGroup(msg.getSecurityGroupUuid(), vmNicUuids);
}
detachSecurityGroupFromL3Network(msg.getSecurityGroupUuid(), msg.getL3NetworkUuid());
APIDetachSecurityGroupFromL3NetworkEvent evt = new APIDetachSecurityGroupFromL3NetworkEvent(msg.getId());
SecurityGroupVO vo = dbf.findByUuid(msg.getSecurityGroupUuid(), SecurityGroupVO.class);
evt.setInventory(SecurityGroupInventory.valueOf(vo));
bus.publish(evt);
}
private void handle(APIChangeSecurityGroupStateMsg msg) {
SecurityGroupStateEvent sevt = SecurityGroupStateEvent.valueOf(msg.getStateEvent());
SecurityGroupVO vo = dbf.findByUuid(msg.getUuid(), SecurityGroupVO.class);
if (sevt == SecurityGroupStateEvent.enable) {
vo.setState(SecurityGroupState.Enabled);
} else {
vo.setState(SecurityGroupState.Disabled);
}
vo = dbf.updateAndRefresh(vo);
APIChangeSecurityGroupStateEvent evt = new APIChangeSecurityGroupStateEvent(msg.getId());
evt.setInventory(SecurityGroupInventory.valueOf(vo));
bus.publish(evt);
}
private void handle(APIQueryVmNicInSecurityGroupMsg msg) {
List<VmNicSecurityGroupRefInventory> invs = qf.query(msg, VmNicSecurityGroupRefInventory.class);
APIQueryVmNicInSecurityGroupReply reply = new APIQueryVmNicInSecurityGroupReply();
reply.setInventories(invs);
bus.reply(msg, reply);
}
private void handle(APIAttachSecurityGroupToL3NetworkMsg msg) {
APIAttachSecurityGroupToL3NetworkEvent evt = new APIAttachSecurityGroupToL3NetworkEvent(msg.getId());
SimpleQuery<SecurityGroupL3NetworkRefVO> q = dbf.createQuery(SecurityGroupL3NetworkRefVO.class);
q.add(SecurityGroupL3NetworkRefVO_.l3NetworkUuid, Op.EQ, msg.getL3NetworkUuid());
q.add(SecurityGroupL3NetworkRefVO_.securityGroupUuid, Op.EQ, msg.getSecurityGroupUuid());
SecurityGroupL3NetworkRefVO ref = q.find();
if (ref == null) {
ref = new SecurityGroupL3NetworkRefVO();
ref.setUuid(Platform.getUuid());
ref.setL3NetworkUuid(msg.getL3NetworkUuid());
ref.setSecurityGroupUuid(msg.getSecurityGroupUuid());
dbf.persist(ref);
}
SecurityGroupVO sgvo = dbf.findByUuid(msg.getSecurityGroupUuid(), SecurityGroupVO.class);
SecurityGroupInventory sginv = SecurityGroupInventory.valueOf(sgvo);
evt.setInventory(sginv);
bus.publish(evt);
}
private void handle(APIListVmNicInSecurityGroupMsg msg) {
List<VmNicSecurityGroupRefVO> vos = dl.listByApiMessage(msg, VmNicSecurityGroupRefVO.class);
List<VmNicSecurityGroupRefInventory> invs = VmNicSecurityGroupRefInventory.valueOf(vos);
APIListVmNicInSecurityGroupReply reply = new APIListVmNicInSecurityGroupReply();
reply.setInventories(invs);
bus.reply(msg, reply);
}
private void removeNicFromSecurityGroup(String sgUuid, List<String> vmNicUuids) {
SimpleQuery<VmNicSecurityGroupRefVO> q = dbf.createQuery(VmNicSecurityGroupRefVO.class);
q.add(VmNicSecurityGroupRefVO_.securityGroupUuid, Op.EQ, sgUuid);
q.add(VmNicSecurityGroupRefVO_.vmNicUuid, Op.IN, vmNicUuids);
List<VmNicSecurityGroupRefVO> refVOs = q.list();
dbf.removeCollection(refVOs, VmNicSecurityGroupRefVO.class);
SimpleQuery<VmNicVO> l3Query = dbf.createQuery(VmNicVO.class);
l3Query.select(VmNicVO_.l3NetworkUuid);
l3Query.add(VmNicVO_.uuid, Op.IN, vmNicUuids);
List<String> l3Uuids = l3Query.listValue();
RuleCalculator cal1 = new RuleCalculator();
cal1.l3NetworkUuids = l3Uuids;
cal1.securityGroupUuids = asList(sgUuid);
List<HostRuleTO> htos1 = cal1.calculate();
// nics may be in other security group
RuleCalculator cal2 = new RuleCalculator();
cal2.vmNicUuids = vmNicUuids;
List<HostRuleTO> htos2 = cal2.calculate();
// create deleting chain action for nics no longer in any security group
SimpleQuery<VmNicSecurityGroupRefVO> refq = dbf.createQuery(VmNicSecurityGroupRefVO.class);
refq.select(VmNicSecurityGroupRefVO_.vmNicUuid);
refq.add(VmNicSecurityGroupRefVO_.vmNicUuid, Op.IN, vmNicUuids);
List<String> nicUuidsIn = refq.listValue();
List<String> nicsUuidsCopy = new ArrayList<String>();
nicsUuidsCopy.addAll(vmNicUuids);
nicsUuidsCopy.removeAll(nicUuidsIn);
Collection<HostRuleTO> htos3 = new ArrayList<HostRuleTO>();
if (!nicsUuidsCopy.isEmpty()) {
htos3 = cal2.createRulePlaceHolder(nicsUuidsCopy);
for (HostRuleTO hto : htos3) {
hto.setActionCodeForAllSecurityGroupRuleTOs(SecurityGroupRuleTO.ACTION_CODE_DELETE_CHAIN);
}
}
List<HostRuleTO> finalHtos = cal2.mergeMultiHostRuleTO(htos1, htos2, htos3);
applyRules(finalHtos);
}
private void handle(APIDeleteVmNicFromSecurityGroupMsg msg) {
removeNicFromSecurityGroup(msg.getSecurityGroupUuid(), msg.getVmNicUuids());
APIDeleteVmNicFromSecurityGroupEvent evt = new APIDeleteVmNicFromSecurityGroupEvent(msg.getId());
bus.publish(evt);
}
private void handle(APIDeleteSecurityGroupMsg msg) {
SimpleQuery<VmNicSecurityGroupRefVO> q = dbf.createQuery(VmNicSecurityGroupRefVO.class);
q.select(VmNicSecurityGroupRefVO_.vmNicUuid);
q.add(VmNicSecurityGroupRefVO_.securityGroupUuid, Op.EQ, msg.getUuid());
List<String> vmNicUuids = q.listValue();
dbf.removeByPrimaryKey(msg.getUuid(), SecurityGroupVO.class);
if (!vmNicUuids.isEmpty()) {
RuleCalculator cal = new RuleCalculator();
cal.vmNicUuids = vmNicUuids;
cal.vmStates = asList(VmInstanceState.Running);
List<HostRuleTO> htos = cal.calculate();
SimpleQuery<VmNicSecurityGroupRefVO> refq = dbf.createQuery(VmNicSecurityGroupRefVO.class);
refq.select(VmNicSecurityGroupRefVO_.vmNicUuid);
refq.add(VmNicSecurityGroupRefVO_.vmNicUuid, Op.IN, vmNicUuids);
List<String> nicUuidsIn = refq.listValue();
vmNicUuids.removeAll(nicUuidsIn);
if (!vmNicUuids.isEmpty()) {
// these vm nics are no longer in any security group, delete their chains on host
Collection<HostRuleTO> toRemove = cal.createRulePlaceHolder(vmNicUuids);
for (HostRuleTO hto : toRemove) {
hto.setActionCodeForAllSecurityGroupRuleTOs(SecurityGroupRuleTO.ACTION_CODE_DELETE_CHAIN);
}
htos = cal.mergeMultiHostRuleTO(htos, toRemove);
}
applyRules(htos);
}
APIDeleteSecurityGroupEvent evt = new APIDeleteSecurityGroupEvent(msg.getId());
logger.debug(String.format("successfully deleted security group[uuid:%s]", msg.getUuid()));
bus.publish(evt);
}
private void handle(APIDeleteSecurityGroupRuleMsg msg) {
SimpleQuery<SecurityGroupRuleVO> q = dbf.createQuery(SecurityGroupRuleVO.class);
q.select(SecurityGroupRuleVO_.securityGroupUuid);
q.add(SecurityGroupRuleVO_.uuid, Op.EQ, msg.getRuleUuids().get(0));
String sgUuid = q.findValue();
dbf.removeByPrimaryKeys(msg.getRuleUuids(), SecurityGroupRuleVO.class);
RuleCalculator cal = new RuleCalculator();
cal.securityGroupUuids = asList(sgUuid);
cal.vmStates = asList(VmInstanceState.Running);
List<HostRuleTO> htos = cal.calculate();
applyRules(htos);
SecurityGroupVO sgvo = dbf.findByUuid(sgUuid, SecurityGroupVO.class);
APIDeleteSecurityGroupRuleEvent evt = new APIDeleteSecurityGroupRuleEvent(msg.getId());
evt.setInventory(SecurityGroupInventory.valueOf(sgvo));
bus.publish(evt);
}
private void handle(final APIAddVmNicToSecurityGroupMsg msg) {
APIAddVmNicToSecurityGroupEvent evt = new APIAddVmNicToSecurityGroupEvent(msg.getId());
SimpleQuery<VmNicVO> q = dbf.createQuery(VmNicVO.class);
q.add(VmNicVO_.uuid, Op.IN, msg.getVmNicUuids());
List<VmNicVO> nicvos = q.list();
List<String> l3Uuids = new ArrayList<String>();
List<String> vmUuids = new ArrayList<String>();
List<VmNicSecurityGroupRefVO> refs = new ArrayList<VmNicSecurityGroupRefVO>();
for (VmNicVO nic : nicvos) {
VmNicSecurityGroupRefVO vo = new VmNicSecurityGroupRefVO();
vo.setSecurityGroupUuid(msg.getSecurityGroupUuid());
vo.setVmInstanceUuid(nic.getVmInstanceUuid());
vo.setVmNicUuid(nic.getUuid());
vo.setUuid(Platform.getUuid());
refs.add(vo);
l3Uuids.add(nic.getL3NetworkUuid());
vmUuids.add(nic.getVmInstanceUuid());
}
dbf.persistCollection(refs);
SimpleQuery<VmInstanceVO> vmq = dbf.createQuery(VmInstanceVO.class);
vmq.add(VmInstanceVO_.uuid, Op.IN, vmUuids);
vmq.add(VmInstanceVO_.state, Op.EQ, VmInstanceState.Running);
boolean triggerApplyRules = vmq.count() > 0;
if (triggerApplyRules) {
RuleCalculator cal = new RuleCalculator();
cal.securityGroupUuids = asList(msg.getSecurityGroupUuid());
cal.l3NetworkUuids = l3Uuids;
cal.vmStates = asList(VmInstanceState.Running);
List<HostRuleTO> htos = cal.calculate();
applyRules(htos);
}
logger.debug(String.format("successfully added vm nics%s to security group[uuid:%s]", msg.getVmNicUuids(), msg.getSecurityGroupUuid()));
bus.publish(evt);
}
private void applyRules(Collection<HostRuleTO> htos) {
for (final HostRuleTO h : htos) {
SecurityGroupHypervisorBackend bkend = hypervisorBackends.get(h.getHypervisorType());
bkend.applyRules(h, new Completion(null) {
private void copeWithFailureHost() {
createFailureHostTask(h.getHostUuid());
}
@Override
public void success() {
logger.debug(String.format("successfully applied security rules on host[uuid:%s]", h.getHostUuid()));
}
@Override
public void fail(ErrorCode errorCode) {
logger.debug(String.format("failed to apply security rules on host[uuid:%s], because %s, will try it later", h.getHostUuid(), errorCode));
copeWithFailureHost();
}
});
}
}
private void handle(APIAddSecurityGroupRuleMsg msg) {
APIAddSecurityGroupRuleEvent evt = new APIAddSecurityGroupRuleEvent(msg.getId());
List<SecurityGroupRuleVO> vos = new ArrayList<SecurityGroupRuleVO>();
for (SecurityGroupRuleAO ao : msg.getRules()) {
SecurityGroupRuleVO vo = new SecurityGroupRuleVO();
vo.setUuid(Platform.getUuid());
vo.setAllowedCidr(ao.getAllowedCidr());
vo.setEndPort(ao.getEndPort());
vo.setStartPort(ao.getStartPort());
vo.setProtocol(SecurityGroupRuleProtocolType.valueOf(ao.getProtocol()));
vo.setType(SecurityGroupRuleType.valueOf(ao.getType()));
vo.setSecurityGroupUuid(msg.getSecurityGroupUuid());
vos.add(vo);
}
dbf.persistCollection(vos);
RuleCalculator cal = new RuleCalculator();
cal.securityGroupUuids = asList(msg.getSecurityGroupUuid());
cal.vmStates = asList(VmInstanceState.Running);
List<HostRuleTO> htos = cal.calculate();
applyRules(htos);
SecurityGroupVO sgvo = dbf.findByUuid(msg.getSecurityGroupUuid(), SecurityGroupVO.class);
evt.setInventory(SecurityGroupInventory.valueOf(sgvo));
logger.debug(String.format("successfully add rules to security group[uuid:%s, name:%s]:\n%s", sgvo.getUuid(), sgvo.getName(), JSONObjectUtil.toJsonString(msg.getRules())));
bus.publish(evt);
}
private void handle(APIListSecurityGroupMsg msg) {
List<SecurityGroupVO> vos = dl.listByApiMessage(msg, SecurityGroupVO.class);
List<SecurityGroupInventory> invs = SecurityGroupInventory.valueOf(vos);
APIListSecurityGroupReply reply = new APIListSecurityGroupReply();
reply.setInventories(invs);
bus.reply(msg, reply);
}
private void handle(APICreateSecurityGroupMsg msg) {
SecurityGroupVO vo = new SecurityGroupVO();
if (msg.getResourceUuid() != null) {
vo.setUuid(msg.getResourceUuid());
} else {
vo.setUuid(Platform.getUuid());
}
vo.setName(msg.getName());
vo.setDescription(msg.getDescription());
vo.setState(SecurityGroupState.Enabled);
vo.setInternalId(dbf.generateSequenceNumber(SecurityGroupSequenceNumberVO.class));
SecurityGroupVO finalVo = vo;
vo = new SQLBatchWithReturn<SecurityGroupVO>() {
@Override
protected SecurityGroupVO scripts() {
persist(finalVo);
reload(finalVo);
acntMgr.createAccountResourceRef(msg.getSession().getAccountUuid(), finalVo.getUuid(), SecurityGroupVO.class);
tagMgr.createTagsFromAPICreateMessage(msg, finalVo.getUuid(), SecurityGroupVO.class.getSimpleName());
return finalVo;
}
}.execute();
SecurityGroupInventory inv = SecurityGroupInventory.valueOf(vo);
APICreateSecurityGroupEvent evt = new APICreateSecurityGroupEvent(msg.getId());
evt.setInventory(inv);
logger.debug(String.format("successfully created security group[uuid:%s, name:%s]", vo.getUuid(), vo.getName()));
bus.publish(evt);
}
public String getId() {
return bus.makeLocalServiceId(SecurityGroupConstant.SERVICE_ID);
}
private void populateExtensions() {
hypervisorBackends = new HashMap<String, SecurityGroupHypervisorBackend>();
for (SecurityGroupHypervisorBackend backend : pluginRgty.getExtensionList(SecurityGroupHypervisorBackend.class)) {
SecurityGroupHypervisorBackend old = hypervisorBackends.get(backend.getSecurityGroupBackendHypervisorType().toString());
if (old != null) {
throw new CloudRuntimeException(String.format("duplicate SecurityGroupHypervisorBackend[%s, %s] for type[%s]",
backend.getClass().getName(), old.getClass().getName(), old.getSecurityGroupBackendHypervisorType()));
}
hypervisorBackends.put(backend.getSecurityGroupBackendHypervisorType().toString(), backend);
}
}
private void startFailureHostCopingThread() {
failureHostCopingThread = thdf.submitPeriodicTask(new FailureHostWorker());
logger.debug(String.format("security group failureHostCopingThread starts[failureHostEachTimeTake: %s, failureHostWorkerInterval: %ss]", failureHostEachTimeTake, failureHostWorkerInterval));
}
private void restartFailureHostCopingThread() {
if (failureHostCopingThread != null) {
failureHostCopingThread.cancel(true);
}
startFailureHostCopingThread();
}
private void prepareGlobalConfig() {
failureHostWorkerInterval = SecurityGroupGlobalConfig.FAILURE_HOST_WORKER_INTERVAL.value(Integer.class);
failureHostEachTimeTake = SecurityGroupGlobalConfig.FAILURE_HOST_EACH_TIME_TO_TAKE.value(Integer.class);
GlobalConfigUpdateExtensionPoint onUpdate = new GlobalConfigUpdateExtensionPoint() {
@Override
public void updateGlobalConfig(GlobalConfig oldConfig, GlobalConfig newConfig) {
if (SecurityGroupGlobalConfig.FAILURE_HOST_EACH_TIME_TO_TAKE.isMe(newConfig)) {
failureHostEachTimeTake = newConfig.value(Integer.class);
restartFailureHostCopingThread();
} else if (SecurityGroupGlobalConfig.FAILURE_HOST_WORKER_INTERVAL.isMe(newConfig)) {
failureHostWorkerInterval = newConfig.value(Integer.class);
restartFailureHostCopingThread();
}
}
};
SecurityGroupGlobalConfig.FAILURE_HOST_WORKER_INTERVAL.installUpdateExtension(onUpdate);
SecurityGroupGlobalConfig.FAILURE_HOST_EACH_TIME_TO_TAKE.installUpdateExtension(onUpdate);
SecurityGroupGlobalConfig.DELAY_REFRESH_INTERVAL.installUpdateExtension(onUpdate);
}
public boolean start() {
prepareGlobalConfig();
populateExtensions();
return true;
}
public boolean stop() {
return true;
}
public SecurityGroupHypervisorBackend getHypervisorBackend(String hypervisorType) {
SecurityGroupHypervisorBackend backend = hypervisorBackends.get(hypervisorType);
if (backend == null) {
throw new CloudRuntimeException(String.format("cannot get security group hypervisor backend[hypervisorType:%s]", hypervisorType));
}
return backend;
}
@Override
public void preMigrateVm(VmInstanceInventory inv, String destHostUuid) {
}
@Override
public void beforeMigrateVm(VmInstanceInventory inv, String destHostUuid) {
}
@Override
public void afterMigrateVm(final VmInstanceInventory inv, final String srcHostUuid) {
RuleCalculator cal = new RuleCalculator();
cal.vmNicUuids = CollectionUtils.transformToList(inv.getVmNics(), new Function<String, VmNicInventory>() {
@Override
public String call(VmNicInventory arg) {
return arg.getUuid();
}
});
cal.vmStates = asList(VmInstanceState.Running);
List<HostRuleTO> htos = cal.calculate();
applyRules(htos);
SecurityGroupHypervisorBackend bkd = getHypervisorBackend(inv.getHypervisorType());
bkd.cleanUpUnusedRuleOnHost(inv.getLastHostUuid(), new Completion(null) {
@Override
public void success() {
logger.debug(String.format("vm[uuid:%s, name:%s] migrated to host[uuid:%s], cleanup its old rules on host[uuid:%s] if needed",
inv.getUuid(), inv.getName(), inv.getHostUuid(), srcHostUuid));
}
@Override
public void fail(ErrorCode errorCode) {
logger.debug(String.format("vm[uuid:%s, name:%s] migrated to host[uuid:%s], failed to cleanup its old rules on host[uuid:%s] if needed",
inv.getUuid(), inv.getName(), inv.getHostUuid(), srcHostUuid));
createFailureHostTask(inv.getLastHostUuid());
}
});
}
@Override
public void failedToMigrateVm(final VmInstanceInventory inv, final String destHostUuid, ErrorCode reason) {
RuleCalculator cal = new RuleCalculator();
cal.vmNicUuids = CollectionUtils.transformToList(inv.getVmNics(), new Function<String, VmNicInventory>() {
@Override
public String call(VmNicInventory arg) {
return arg.getUuid();
}
});
cal.vmStates = asList(VmInstanceState.Unknown);
List<HostRuleTO> htos = cal.calculate();
logger.debug(String.format("vm[uuid:%s, name:%s] failed to migrate to host[uuid:%s], recover its rules on previous host[uuid:%s]",
inv.getUuid(), inv.getName(), destHostUuid, inv.getHostUuid()));
applyRules(htos);
}
@Override
public List<ExpandedQueryStruct> getExpandedQueryStructs() {
List<ExpandedQueryStruct> structs = new ArrayList<ExpandedQueryStruct>();
ExpandedQueryStruct struct = new ExpandedQueryStruct();
struct.setExpandedField("securityGroupRef");
struct.setHidden(true);
struct.setExpandedInventoryKey("vmNicUuid");
struct.setForeignKey("uuid");
struct.setInventoryClass(VmNicSecurityGroupRefInventory.class);
struct.setInventoryClassToExpand(VmNicInventory.class);
structs.add(struct);
return structs;
}
@Override
public List<ExpandedQueryAliasStruct> getExpandedQueryAliasesStructs() {
List<ExpandedQueryAliasStruct> aliases = new ArrayList<ExpandedQueryAliasStruct>();
ExpandedQueryAliasStruct as = new ExpandedQueryAliasStruct();
as.setInventoryClass(VmNicInventory.class);
as.setAlias("securityGroup");
as.setExpandedField("securityGroupRef.securityGroup");
aliases.add(as);
return aliases;
}
private class FailureHostWorker implements PeriodicTask {
@Transactional
private List<SecurityGroupFailureHostVO> takeFailureHosts() {
String sql = "select sgf from SecurityGroupFailureHostVO sgf, HostVO host where host.uuid = sgf.hostUuid and host.status = :hostConnectionState and sgf.managementNodeId is NULL group by sgf.hostUuid order by sgf.lastOpDate ASC";
TypedQuery<SecurityGroupFailureHostVO> q = dbf.getEntityManager().createQuery(sql, SecurityGroupFailureHostVO.class);
q.setLockMode(LockModeType.PESSIMISTIC_READ);
q.setParameter("hostConnectionState", HostStatus.Connected);
q.setMaxResults(failureHostEachTimeTake);
List<SecurityGroupFailureHostVO> lst = q.getResultList();
if (lst.isEmpty()) {
return lst;
}
List<Long> ids = CollectionUtils.transformToList(lst, new Function<Long, SecurityGroupFailureHostVO>() {
@Override
public Long call(SecurityGroupFailureHostVO arg) {
return arg.getId();
}
});
sql = "update SecurityGroupFailureHostVO f set f.managementNodeId = :mgmtId where f.id in (:ids)";
Query uq = dbf.getEntityManager().createQuery(sql);
uq.setParameter("mgmtId", Platform.getManagementServerId());
uq.setParameter("ids", ids);
uq.executeUpdate();
return lst;
}
private void copeWithFailureHost(SecurityGroupFailureHostVO fvo) {
fvo.setManagementNodeId(null);
dbf.update(fvo);
}
@Override
public void run() {
List<SecurityGroupFailureHostVO> vos = takeFailureHosts();
if (vos.isEmpty()) {
return;
}
for (final SecurityGroupFailureHostVO vo : vos) {
RuleCalculator cal = new RuleCalculator();
cal.hostUuids = asList(vo.getHostUuid());
cal.vmStates = asList(VmInstanceState.Running);
List<HostRuleTO> htos = cal.calculate();
if (htos.isEmpty()) {
logger.debug(String.format("no security rules needs to be applied to the host[uuid:%s], clean up it" +
" from SecurityGroupFailureHostVO", vo.getHostUuid()));
dbf.remove(vo);
continue;
}
final HostRuleTO hto = htos.get(0);
hto.setRefreshHost(true);
SecurityGroupHypervisorBackend bd = getHypervisorBackend(hto.getHypervisorType());
bd.applyRules(hto, new Completion(null) {
@Override
public void success() {
logger.debug(String.format("successfully re-apply security group rules to host[uuid:%s]", hto.getHostUuid()));
dbf.remove(vo);
}
@Override
public void fail(ErrorCode errorCode) {
logger.debug(String.format("failed to re-apply security group rules to host[uuid:%s], because %s, try it later", hto.getHostUuid(), errorCode));
copeWithFailureHost(vo);
}
});
}
}
@Override
public TimeUnit getTimeUnit() {
return TimeUnit.SECONDS;
}
@Override
public long getInterval() {
return failureHostWorkerInterval;
}
@Override
public String getName() {
return FailureHostWorker.class.getName();
}
}
}