| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| package com.cloud.api.query.dao; | |
| import java.util.ArrayList; | |
| import java.util.Collections; | |
| import java.util.Comparator; | |
| import java.util.HashMap; | |
| import java.util.List; | |
| import java.util.Map; | |
| import javax.inject.Inject; | |
| import com.cloud.api.ApiDBUtils; | |
| import com.cloud.api.ApiResponseHelper; | |
| import com.cloud.api.query.vo.SnapshotJoinVO; | |
| import com.cloud.storage.GuestOS; | |
| import com.cloud.storage.Snapshot; | |
| import com.cloud.storage.VMTemplateStorageResourceAssoc; | |
| import com.cloud.storage.Volume.Type; | |
| import com.cloud.storage.VolumeVO; | |
| import com.cloud.storage.snapshot.SnapshotManager; | |
| import com.cloud.user.Account; | |
| import com.cloud.user.AccountService; | |
| import com.cloud.utils.db.Filter; | |
| import com.cloud.utils.db.SearchBuilder; | |
| import com.cloud.utils.db.SearchCriteria; | |
| import com.cloud.vm.VMInstanceVO; | |
| import org.apache.cloudstack.annotation.AnnotationService; | |
| import org.apache.cloudstack.annotation.dao.AnnotationDao; | |
| import org.apache.cloudstack.api.ResponseObject; | |
| import org.apache.cloudstack.api.response.SnapshotResponse; | |
| import org.apache.cloudstack.context.CallContext; | |
| import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; | |
| import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; | |
| import org.apache.cloudstack.framework.config.dao.ConfigurationDao; | |
| import org.apache.cloudstack.query.QueryService; | |
| import org.apache.commons.collections.CollectionUtils; | |
| public class SnapshotJoinDaoImpl extends GenericDaoBaseWithTagInformation<SnapshotJoinVO, SnapshotResponse> implements SnapshotJoinDao { | |
| @Inject | |
| AccountService accountService; | |
| @Inject | |
| AnnotationDao annotationDao; | |
| @Inject | |
| ConfigurationDao configDao; | |
| @Inject | |
| SnapshotDataFactory snapshotDataFactory; | |
| private final SearchBuilder<SnapshotJoinVO> snapshotStorePairSearch; | |
| private final SearchBuilder<SnapshotJoinVO> snapshotIdsSearch; | |
| private final SearchBuilder<SnapshotJoinVO> snapshotByZoneSearch; | |
| SnapshotJoinDaoImpl() { | |
| snapshotStorePairSearch = createSearchBuilder(); | |
| snapshotStorePairSearch.and("snapshotStoreState", snapshotStorePairSearch.entity().getStoreState(), SearchCriteria.Op.IN); | |
| snapshotStorePairSearch.and("snapshotStoreIdIN", snapshotStorePairSearch.entity().getSnapshotStorePair(), SearchCriteria.Op.IN); | |
| snapshotStorePairSearch.done(); | |
| snapshotIdsSearch = createSearchBuilder(); | |
| snapshotIdsSearch.and("zoneId", snapshotIdsSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); | |
| snapshotIdsSearch.and("idsIN", snapshotIdsSearch.entity().getId(), SearchCriteria.Op.IN); | |
| snapshotIdsSearch.groupBy(snapshotIdsSearch.entity().getId()); | |
| snapshotIdsSearch.done(); | |
| snapshotByZoneSearch = createSearchBuilder(); | |
| snapshotByZoneSearch.and("zoneId", snapshotByZoneSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); | |
| snapshotByZoneSearch.and("id", snapshotByZoneSearch.entity().getId(), SearchCriteria.Op.EQ); | |
| snapshotByZoneSearch.done(); | |
| } | |
| private void setSnapshotInfoDetailsInResponse(SnapshotJoinVO snapshot, SnapshotResponse snapshotResponse, boolean isShowUnique) { | |
| if (!isShowUnique) { | |
| return; | |
| } | |
| if (snapshot.getDataCenterId() == null) { | |
| return; | |
| } | |
| SnapshotInfo snapshotInfo; | |
| snapshotInfo = snapshotDataFactory.getSnapshotWithRoleAndZone(snapshot.getId(), snapshot.getStoreRole(), snapshot.getDataCenterId()); | |
| if (snapshotInfo == null) { | |
| logger.debug("Unable to find info for image store snapshot {}", snapshot); | |
| snapshotResponse.setRevertable(false); | |
| } else { | |
| snapshotResponse.setRevertable(snapshotInfo.isRevertable()); | |
| snapshotResponse.setPhysicalSize(snapshotInfo.getPhysicalSize()); | |
| boolean showChainSize = SnapshotManager.snapshotShowChainSize.valueIn(snapshot.getDataCenterId()); | |
| if (showChainSize && snapshotInfo.getParent() != null) { | |
| long chainSize = calculateChainSize(snapshotInfo); | |
| snapshotResponse.setChainSize(chainSize); | |
| snapshotResponse.setParent(snapshotInfo.getParent().getUuid()); | |
| snapshotResponse.setParentName(snapshotInfo.getParent().getName()); | |
| } | |
| } | |
| } | |
| private long calculateChainSize(SnapshotInfo snapshotInfo) { | |
| long chainSize = snapshotInfo.getPhysicalSize(); | |
| SnapshotInfo parent = snapshotInfo.getParent(); | |
| while (parent != null) { | |
| chainSize += parent.getPhysicalSize(); | |
| parent = parent.getParent(); | |
| } | |
| return chainSize; | |
| } | |
| private String getSnapshotStatus(SnapshotJoinVO snapshot) { | |
| String status = snapshot.getStatus().toString(); | |
| if (snapshot.getDownloadState() == null) { | |
| return status; | |
| } | |
| if (snapshot.getDownloadState() != VMTemplateStorageResourceAssoc.Status.DOWNLOADED) { | |
| status = "Processing"; | |
| if (snapshot.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS) { | |
| status = snapshot.getDownloadPercent() + "% Downloaded"; | |
| } else if (snapshot.getErrorString() == null) { | |
| status = snapshot.getStoreState().toString(); | |
| } else { | |
| status = snapshot.getErrorString(); | |
| } | |
| } | |
| return status; | |
| } | |
| @Override | |
| public SnapshotResponse newSnapshotResponse(ResponseObject.ResponseView view, boolean isShowUnique, SnapshotJoinVO snapshot) { | |
| final Account caller = CallContext.current().getCallingAccount(); | |
| SnapshotResponse snapshotResponse = new SnapshotResponse(); | |
| snapshotResponse.setId(snapshot.getUuid()); | |
| // populate owner. | |
| ApiResponseHelper.populateOwner(snapshotResponse, snapshot); | |
| if (snapshot.getVolumeId() != null) { | |
| snapshotResponse.setVolumeId(snapshot.getVolumeUuid()); | |
| snapshotResponse.setVolumeName(snapshot.getVolumeName()); | |
| snapshotResponse.setVolumeType(snapshot.getVolumeType().name()); | |
| snapshotResponse.setVolumeState(snapshot.getVolumeState().name()); | |
| snapshotResponse.setVirtualSize(snapshot.getVolumeSize()); | |
| VolumeVO volume = ApiDBUtils.findVolumeById(snapshot.getVolumeId()); | |
| if (volume != null && volume.getVolumeType() == Type.ROOT && volume.getInstanceId() != null) { | |
| VMInstanceVO vm = ApiDBUtils.findVMInstanceById(volume.getInstanceId()); | |
| if (vm != null) { | |
| GuestOS guestOS = ApiDBUtils.findGuestOSById(vm.getGuestOSId()); | |
| if (guestOS != null) { | |
| snapshotResponse.setOsTypeId(guestOS.getUuid()); | |
| } | |
| } | |
| } | |
| } | |
| snapshotResponse.setZoneId(snapshot.getDataCenterUuid()); | |
| snapshotResponse.setZoneName(snapshot.getDataCenterName()); | |
| snapshotResponse.setCreated(snapshot.getCreated()); | |
| snapshotResponse.setName(snapshot.getName()); | |
| String intervalType = null; | |
| if (snapshot.getSnapshotType() >= 0 && snapshot.getSnapshotType() < Snapshot.Type.values().length) { | |
| intervalType = Snapshot.Type.values()[snapshot.getSnapshotType()].name(); | |
| } | |
| snapshotResponse.setIntervalType(intervalType); | |
| snapshotResponse.setState(snapshot.getStatus()); | |
| snapshotResponse.setLocationType(snapshot.getLocationType() != null ? snapshot.getLocationType().name() : null); | |
| if (!isShowUnique) { | |
| snapshotResponse.setDatastoreState(snapshot.getStoreState() != null ? snapshot.getStoreState().name() : null); | |
| if (view.equals(ResponseObject.ResponseView.Full)) { | |
| snapshotResponse.setDatastoreId(snapshot.getStoreUuid()); | |
| snapshotResponse.setDatastoreName(snapshot.getStoreName()); | |
| snapshotResponse.setDatastoreType(snapshot.getStoreRole() != null ? snapshot.getStoreRole().name() : null); | |
| } | |
| // If the user is an 'Admin' or 'the owner of template' or template belongs to a project, add the template download status | |
| if (view == ResponseObject.ResponseView.Full || | |
| snapshot.getAccountId() == caller.getId() || | |
| snapshot.getAccountType() == Account.Type.PROJECT) { | |
| String status = getSnapshotStatus(snapshot); | |
| if (status != null) { | |
| snapshotResponse.setStatus(status); | |
| } | |
| } | |
| Map<String, String> downloadDetails = new HashMap<>(); | |
| downloadDetails.put("downloadPercent", Integer.toString(snapshot.getDownloadPercent())); | |
| downloadDetails.put("downloadState", (snapshot.getDownloadState() != null ? snapshot.getDownloadState().toString() : "")); | |
| snapshotResponse.setDownloadDetails(downloadDetails); | |
| } | |
| setSnapshotInfoDetailsInResponse(snapshot, snapshotResponse, isShowUnique); | |
| setSnapshotResponse(snapshotResponse, snapshot); | |
| snapshotResponse.setObjectName("snapshot"); | |
| return snapshotResponse; | |
| } | |
| @Override | |
| public SnapshotResponse setSnapshotResponse(SnapshotResponse snapshotResponse, SnapshotJoinVO snapshot) { | |
| // update tag information | |
| long tag_id = snapshot.getTagId(); | |
| if (tag_id > 0) { | |
| addTagInformation(snapshot, snapshotResponse); | |
| } | |
| if (snapshotResponse.hasAnnotation() == null) { | |
| snapshotResponse.setHasAnnotation(annotationDao.hasAnnotations(snapshot.getUuid(), AnnotationService.EntityType.SNAPSHOT.name(), | |
| accountService.isRootAdmin(CallContext.current().getCallingAccount().getId()))); | |
| } | |
| return snapshotResponse; | |
| } | |
| @Override | |
| public List<SnapshotJoinVO> searchBySnapshotStorePair(String... pairs) { | |
| // set detail batch query size | |
| int DETAILS_BATCH_SIZE = 2000; | |
| String batchCfg = configDao.getValue("detail.batch.query.size"); | |
| if (batchCfg != null) { | |
| DETAILS_BATCH_SIZE = Integer.parseInt(batchCfg); | |
| } | |
| // query details by batches | |
| Filter searchFilter = new Filter(SnapshotJoinVO.class, "snapshotStorePair", QueryService.SortKeyAscending.value(), null, null); | |
| List<SnapshotJoinVO> uvList = new ArrayList<>(); | |
| // query details by batches | |
| int curr_index = 0; | |
| if (pairs.length > DETAILS_BATCH_SIZE) { | |
| while ((curr_index + DETAILS_BATCH_SIZE) <= pairs.length) { | |
| String[] labels = new String[DETAILS_BATCH_SIZE]; | |
| for (int k = 0, j = curr_index; j < curr_index + DETAILS_BATCH_SIZE; j++, k++) { | |
| labels[k] = pairs[j]; | |
| } | |
| SearchCriteria<SnapshotJoinVO> sc = snapshotStorePairSearch.create(); | |
| sc.setParameters("snapshotStoreIdIN", labels); | |
| List<SnapshotJoinVO> snaps = searchIncludingRemoved(sc, searchFilter, null, false); | |
| if (snaps != null) { | |
| uvList.addAll(snaps); | |
| } | |
| curr_index += DETAILS_BATCH_SIZE; | |
| } | |
| } | |
| if (curr_index < pairs.length) { | |
| int batch_size = (pairs.length - curr_index); | |
| String[] labels = new String[batch_size]; | |
| for (int k = 0, j = curr_index; j < curr_index + batch_size; j++, k++) { | |
| labels[k] = pairs[j]; | |
| } | |
| SearchCriteria<SnapshotJoinVO> sc = snapshotStorePairSearch.create(); | |
| sc.setParameters("snapshotStoreIdIN", labels); | |
| List<SnapshotJoinVO> vms = searchIncludingRemoved(sc, searchFilter, null, false); | |
| if (vms != null) { | |
| uvList.addAll(vms); | |
| } | |
| } | |
| return uvList; | |
| } | |
| protected List<SnapshotJoinVO> findById(Long zoneId, long id) { | |
| SearchBuilder<SnapshotJoinVO> sb = createSearchBuilder(); | |
| sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); | |
| sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); | |
| sb.done(); | |
| SearchCriteria<SnapshotJoinVO> sc = sb.create(); | |
| sc.setParameters("id", id); | |
| if (zoneId != null) { | |
| sc.setParameters("zoneId", zoneId); | |
| } | |
| List<SnapshotJoinVO> snapshotJoinVOS = search(sc, null); | |
| if (CollectionUtils.isEmpty(snapshotJoinVOS)) { | |
| return null; | |
| } | |
| snapshotJoinVOS.sort(Comparator.comparing(SnapshotJoinVO::getSnapshotStorePair)); | |
| return Collections.singletonList(snapshotJoinVOS.get(0)); | |
| } | |
| @Override | |
| public List<SnapshotJoinVO> findByDistinctIds(Long zoneId, Long... ids) { | |
| if (ids == null || ids.length == 0) { | |
| return new ArrayList<>(); | |
| } | |
| if (ids.length == 1) { | |
| return findById(zoneId, ids[0]); | |
| } | |
| Filter searchFilter = new Filter(SnapshotJoinVO.class, "snapshotStorePair", QueryService.SortKeyAscending.value(), null, null); | |
| SearchCriteria<SnapshotJoinVO> sc = snapshotIdsSearch.create(); | |
| if (zoneId != null) { | |
| sc.setParameters("zoneId", zoneId); | |
| } | |
| sc.setParameters("idsIN", ids); | |
| return searchIncludingRemoved(sc, searchFilter, null, false); | |
| } | |
| public List<SnapshotJoinVO> listBySnapshotIdAndZoneId(Long zoneId, Long snapshotId) { | |
| if (snapshotId == null) { | |
| return new ArrayList<>(); | |
| } | |
| SearchCriteria<SnapshotJoinVO> sc = snapshotByZoneSearch.create(); | |
| if (zoneId != null) { | |
| sc.setParameters("zoneId", zoneId); | |
| } | |
| sc.setParameters("id", snapshotId); | |
| return listBy(sc); | |
| } | |
| } |