package org.zstack.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.cascade.AbstractAsyncCascadeExtension;
import org.zstack.core.cascade.CascadeAction;
import org.zstack.core.cascade.CascadeConstant;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusListCallBack;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.header.configuration.ConfigurationConstant;
import org.zstack.header.configuration.InstanceOfferingDeletionMsg;
import org.zstack.header.configuration.InstanceOfferingInventory;
import org.zstack.header.configuration.InstanceOfferingVO;
import org.zstack.header.core.Completion;
import org.zstack.header.exception.CloudRuntimeException;
import org.zstack.header.identity.AccountInventory;
import org.zstack.header.identity.AccountVO;
import org.zstack.header.message.MessageReply;
import org.zstack.utils.CollectionUtils;
import org.zstack.utils.Utils;
import org.zstack.utils.function.Function;
import org.zstack.utils.logging.CLogger;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*/
public class InstanceOfferingCascadeExtension extends AbstractAsyncCascadeExtension {
private static final CLogger logger = Utils.getLogger(InstanceOfferingCascadeExtension.class);
@Autowired
private DatabaseFacade dbf;
@Autowired
private CloudBus bus;
private static final String NAME = InstanceOfferingVO.class.getSimpleName();
@Override
public void asyncCascade(CascadeAction action, Completion completion) {
if (action.isActionCode(CascadeConstant.DELETION_CHECK_CODE)) {
handleDeletionCheck(action, completion);
} else if (action.isActionCode(CascadeConstant.DELETION_DELETE_CODE, CascadeConstant.DELETION_FORCE_DELETE_CODE)) {
handleDeletion(action, completion);
} else if (action.isActionCode(CascadeConstant.DELETION_CLEANUP_CODE)) {
handleDeletionCleanup(action, completion);
} else {
completion.success();
}
}
private void handleDeletionCleanup(CascadeAction action, Completion completion) {
deleteInstanceOfferingEONotReferredByVm();
completion.success();
}
private void deleteInstanceOfferingEONotReferredByVm() {
String sql = "select i.uuid from InstanceOfferingEO i where i.deleted is not null and i.uuid not in (select vm.instanceOfferingUuid from VmInstanceVO vm where vm.instanceOfferingUuid is not null)";
dbf.hardDeleteCollectionSelectedBySQL(sql, InstanceOfferingVO.class);
}
private void handleDeletion(final CascadeAction action, final Completion completion) {
final List<InstanceOfferingInventory> ioinvs = instanceOfferingFromAction(action);
if (ioinvs == null || ioinvs.isEmpty()) {
completion.success();
return;
}
List<InstanceOfferingDeletionMsg> msgs = new ArrayList<InstanceOfferingDeletionMsg>();
for (InstanceOfferingInventory ioinv : ioinvs) {
InstanceOfferingDeletionMsg msg = new InstanceOfferingDeletionMsg();
msg.setInstanceOfferingUuid(ioinv.getUuid());
msg.setForceDelete(action.isActionCode(CascadeConstant.DELETION_FORCE_DELETE_CODE));
bus.makeTargetServiceIdByResourceUuid(msg, ConfigurationConstant.SERVICE_ID, ioinv.getUuid());
msgs.add(msg);
}
bus.send(msgs, new CloudBusListCallBack(completion) {
@Override
public void run(List<MessageReply> replies) {
if (!action.isActionCode(CascadeConstant.DELETION_FORCE_DELETE_CODE)) {
for (MessageReply r : replies) {
if (!r.isSuccess()) {
completion.fail(r.getError());
return;
}
}
}
completion.success();
}
});
}
private void handleDeletionCheck(CascadeAction action, Completion completion) {
completion.success();
}
@Override
public List<String> getEdgeNames() {
return Arrays.asList(AccountVO.class.getSimpleName());
}
@Override
public String getCascadeResourceName() {
return NAME;
}
private List<InstanceOfferingInventory> instanceOfferingFromAction(CascadeAction action) {
if (NAME.equals(action.getParentIssuer())) {
return action.getParentIssuerContext();
} else if (AccountVO.class.getSimpleName().equals(action.getParentIssuer())) {
return getForAccount((List<AccountInventory>) action.getParentIssuerContext());
}
throw new CloudRuntimeException("should not be here");
}
@Transactional(readOnly = true)
private List<InstanceOfferingInventory> getForAccount(List<AccountInventory> invs) {
List<String> accountUuids = CollectionUtils.transformToList(invs, new Function<String, AccountInventory>() {
@Override
public String call(AccountInventory arg) {
return arg.getUuid();
}
});
String sql = "select d from InstanceOfferingVO d, AccountResourceRefVO r where d.uuid = r.resourceUuid and" +
" r.resourceType = :rtype and r.accountUuid in (:auuids)";
TypedQuery<InstanceOfferingVO> q = dbf.getEntityManager().createQuery(sql, InstanceOfferingVO.class);
q.setParameter("rtype", InstanceOfferingVO.class.getSimpleName());
q.setParameter("auuids", accountUuids);
List<InstanceOfferingVO> vos = q.getResultList();
return InstanceOfferingInventory.valueOf(vos);
}
@Override
public CascadeAction createActionForChildResource(CascadeAction action) {
if (CascadeConstant.DELETION_CODES.contains(action.getActionCode())) {
List<InstanceOfferingInventory> ctx = instanceOfferingFromAction(action);
if (ctx != null && !ctx.isEmpty()) {
return action.copy().setParentIssuer(NAME).setParentIssuerContext(ctx);
}
}
return null;
}
}