package org.zstack.image;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.http.HttpHeaders;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.header.apimediator.ApiMessageInterceptionException;
import org.zstack.header.core.BypassWhenUnitTest;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.identity.IdentityErrors;
import org.zstack.header.identity.Quota;
import org.zstack.header.image.APIAddImageMsg;
import org.zstack.header.image.ImageConstant;
import org.zstack.header.image.ImageVO;
import org.zstack.header.message.MessageReply;
import org.zstack.header.rest.RESTFacade;
import org.zstack.header.storage.backup.BackupStorageConstant;
import org.zstack.header.storage.backup.GetImageSizeOnBackupStorageMsg;
import org.zstack.header.storage.backup.GetImageSizeOnBackupStorageReply;
import org.zstack.utils.Utils;
import org.zstack.utils.logging.CLogger;
import javax.persistence.TypedQuery;
import java.util.Map;
/**
* Created by miao on 16-10-9.
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class ImageQuotaUtil {
private static final CLogger logger = Utils.getLogger(ImageQuotaUtil.class);
@Autowired
public DatabaseFacade dbf;
@Autowired
private ErrorFacade errf;
@Autowired
private CloudBus bus;
@Autowired
protected RESTFacade restf;
public class ImageQuota {
public long imageNum;
public long imageSize;
}
@Transactional(readOnly = true)
public ImageQuota getUsed(String accountUUid) {
ImageQuota quota = new ImageQuota();
quota.imageSize = getUsedImageSize(accountUUid);
quota.imageNum = getUsedImageNum(accountUUid);
return quota;
}
@Transactional(readOnly = true)
public long getUsedImageNum(String accountUuid) {
String sql = "select count(image) " +
" from ImageVO image, AccountResourceRefVO ref " +
" where image.uuid = ref.resourceUuid " +
" and ref.accountUuid = :auuid " +
" and ref.resourceType = :rtype ";
TypedQuery<Long> q = dbf.getEntityManager().createQuery(sql, Long.class);
q.setParameter("auuid", accountUuid);
q.setParameter("rtype", ImageVO.class.getSimpleName());
Long imageNum = q.getSingleResult();
imageNum = imageNum == null ? 0 : imageNum;
return imageNum;
}
@Transactional(readOnly = true)
public long getUsedImageSize(String accountUuid) {
String sql = "select sum(image.actualSize) " +
" from ImageVO image ,AccountResourceRefVO ref " +
" where image.uuid = ref.resourceUuid " +
" and ref.accountUuid = :auuid " +
" and ref.resourceType = :rtype ";
TypedQuery<Long> q = dbf.getEntityManager().createQuery(sql, Long.class);
q.setParameter("auuid", accountUuid);
q.setParameter("rtype", ImageVO.class.getSimpleName());
Long imageSize = q.getSingleResult();
imageSize = imageSize == null ? 0 : imageSize;
return imageSize;
}
@BypassWhenUnitTest
public void checkImageSizeQuotaUseHttpHead(APIAddImageMsg msg, Map<String, Quota.QuotaPair> pairs) {
long imageSizeQuota = pairs.get(ImageConstant.QUOTA_IMAGE_SIZE).getValue();
long imageSizeUsed = new ImageQuotaUtil().getUsedImageSize(msg.getSession().getAccountUuid());
long imageSizeAsked;
String url = msg.getUrl();
url = url.trim();
if (url.startsWith("file:///")) {
GetImageSizeOnBackupStorageMsg cmsg = new GetImageSizeOnBackupStorageMsg();
cmsg.setBackupStorageUuid(msg.getBackupStorageUuids().get(0));
cmsg.setImageUrl(url);
cmsg.setImageUuid(msg.getResourceUuid());
bus.makeTargetServiceIdByResourceUuid(cmsg, BackupStorageConstant.SERVICE_ID,
msg.getBackupStorageUuids().get(0));
MessageReply reply = bus.call(cmsg);
if (!reply.isSuccess()) {
throw new OperationFailureException(reply.getError());
}
imageSizeAsked = ((GetImageSizeOnBackupStorageReply) reply).getSize();
} else if (url.startsWith("http") || url.startsWith("https")) {
String len;
try {
HttpHeaders header = restf.getRESTTemplate().headForHeaders(url);
len = header.getFirst("Content-Length");
} catch (Exception e) {
logger.warn(String.format("cannot get image. The image url : %s. description: %s.name: %s",
url, msg.getDescription(), msg.getName()));
return;
}
imageSizeAsked = len == null ? 0 : Long.valueOf(len);
} else {
// bypass the check
imageSizeAsked = 0;
}
if ((imageSizeQuota == 0) || (imageSizeUsed + imageSizeAsked > imageSizeQuota)) {
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(), ImageConstant.QUOTA_IMAGE_SIZE, imageSizeQuota)
));
}
}
}