diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index 42128525782..3063b0d49a2 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.api.query; +import static com.cloud.vm.VmDetailConstants.SSH_PUBLIC_KEY; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; @@ -34,39 +36,6 @@ import java.util.stream.Stream; import javax.inject.Inject; -import com.cloud.network.dao.IPAddressDao; -import com.cloud.network.dao.IPAddressVO; -import com.cloud.storage.StoragePool; -import com.cloud.storage.StoragePoolHostVO; -import com.cloud.event.EventVO; -import com.cloud.event.dao.EventDao; -import com.cloud.host.HostVO; -import com.cloud.offering.ServiceOffering; -import com.cloud.service.ServiceOfferingDetailsVO; -import com.cloud.storage.VMTemplateStoragePoolVO; -import com.cloud.storage.dao.StoragePoolHostDao; -import com.cloud.storage.dao.VMTemplatePoolDao; -import com.cloud.host.Host; -import com.cloud.host.dao.HostDao; -import com.cloud.network.as.AutoScaleVmGroupVmMapVO; -import com.cloud.network.as.dao.AutoScaleVmGroupDao; -import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; -import com.cloud.network.dao.NetworkDao; -import com.cloud.network.dao.NetworkVO; -import com.cloud.network.dao.PublicIpQuarantineDao; -import com.cloud.network.PublicIpQuarantine; -import com.cloud.network.vo.PublicIpQuarantineVO; -import com.cloud.storage.dao.VolumeDao; -import com.cloud.user.AccountVO; -import com.cloud.user.SSHKeyPairVO; -import com.cloud.user.dao.SSHKeyPairDao; -import com.cloud.vm.InstanceGroupVMMapVO; -import com.cloud.vm.NicVO; -import com.cloud.vm.UserVmDetailVO; -import com.cloud.vm.dao.InstanceGroupVMMapDao; -import com.cloud.vm.dao.NicDao; -import com.cloud.vm.dao.UserVmDetailsDao; -import com.cloud.storage.VolumeVO; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker; @@ -108,6 +77,7 @@ import org.apache.cloudstack.api.command.user.account.ListAccountsCmd; import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd; import org.apache.cloudstack.api.command.user.address.ListQuarantinedIpsCmd; import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupsCmd; +import org.apache.cloudstack.api.command.user.bucket.ListBucketsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.cloudstack.api.command.user.iso.ListIsosCmd; import org.apache.cloudstack.api.command.user.job.ListAsyncJobsCmd; @@ -129,6 +99,7 @@ import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd; import org.apache.cloudstack.api.command.user.zone.ListZonesCmd; import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.AsyncJobResponse; +import org.apache.cloudstack.api.response.BucketResponse; import org.apache.cloudstack.api.response.DetailOptionsResponse; import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.DomainResponse; @@ -255,22 +226,38 @@ import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; +import com.cloud.event.EventVO; +import com.cloud.event.dao.EventDao; import com.cloud.event.dao.EventJoinDao; import com.cloud.exception.CloudAuthenticationException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.ha.HighAvailabilityManager; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.PublicIpQuarantine; import com.cloud.network.RouterHealthCheckResult; import com.cloud.network.VNF; import com.cloud.network.VpcVirtualNetworkApplianceService; +import com.cloud.network.as.AutoScaleVmGroupVmMapVO; +import com.cloud.network.as.dao.AutoScaleVmGroupDao; +import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PublicIpQuarantineDao; import com.cloud.network.dao.RouterHealthCheckResultDao; import com.cloud.network.dao.RouterHealthCheckResultVO; import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.security.SecurityGroupVMMapVO; import com.cloud.network.security.dao.SecurityGroupVMMapDao; +import com.cloud.network.vo.PublicIpQuarantineVO; import com.cloud.offering.DiskOffering; +import com.cloud.offering.ServiceOffering; import com.cloud.org.Grouping; import com.cloud.projects.Project; import com.cloud.projects.Project.ListProjectResourcesCriteria; @@ -286,6 +273,7 @@ import com.cloud.server.ResourceManagerUtil; import com.cloud.server.ResourceMetaDataService; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; +import com.cloud.service.ServiceOfferingDetailsVO; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDetailsDao; @@ -298,24 +286,34 @@ import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.TemplateType; +import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolHostVO; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolTagVO; +import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeApiServiceImpl; +import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.BucketDao; import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.StoragePoolTagsDao; import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.dao.VMTemplatePoolDao; +import com.cloud.storage.dao.VolumeDao; import com.cloud.tags.ResourceTagVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.template.VirtualMachineTemplate.State; import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.user.Account; import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; import com.cloud.user.DomainManager; +import com.cloud.user.SSHKeyPairVO; import com.cloud.user.User; import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.SSHKeyPairDao; import com.cloud.user.dao.UserDao; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; @@ -331,18 +329,20 @@ import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.DomainRouterVO; +import com.cloud.vm.InstanceGroupVMMapVO; +import com.cloud.vm.NicVO; +import com.cloud.vm.UserVmDetailVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VmDetailConstants; import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.InstanceGroupVMMapDao; +import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; -import org.apache.cloudstack.api.command.user.bucket.ListBucketsCmd; -import org.apache.cloudstack.api.response.BucketResponse; - -import static com.cloud.vm.VmDetailConstants.SSH_PUBLIC_KEY; @Component public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements QueryService, Configurable { @@ -5627,7 +5627,6 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Integer count = snapshotDataPair.second(); if (count == 0) { - // empty result return snapshotDataPair; } List snapshotData = snapshotDataPair.first(); @@ -5637,7 +5636,6 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q } else { snapshots = snapshotJoinDao.searchBySnapshotStorePair(snapshotData.stream().map(SnapshotJoinVO::getSnapshotStorePair).toArray(String[]::new)); } - return new Pair<>(snapshots, count); } diff --git a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDao.java b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDao.java index 4e916e66ae7..25dfbfe6714 100644 --- a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDao.java +++ b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDao.java @@ -23,10 +23,7 @@ import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.response.SnapshotResponse; import com.cloud.api.query.vo.SnapshotJoinVO; -import com.cloud.utils.Pair; -import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; -import com.cloud.utils.db.SearchCriteria; public interface SnapshotJoinDao extends GenericDao { @@ -34,8 +31,7 @@ public interface SnapshotJoinDao extends GenericDao { SnapshotResponse setSnapshotResponse(SnapshotResponse snapshotResponse, SnapshotJoinVO snapshot); - Pair, Integer> searchIncludingRemovedAndCount(final SearchCriteria sc, final Filter filter); - List searchBySnapshotStorePair(String... pairs); + List findByDistinctIds(Long zoneId, Long... ids); } diff --git a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java index 8b951c174f4..fe462859310 100644 --- a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java @@ -18,6 +18,8 @@ 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; @@ -33,6 +35,7 @@ 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; import org.apache.log4j.Logger; import com.cloud.api.ApiDBUtils; @@ -45,7 +48,6 @@ import com.cloud.storage.Volume.Type; import com.cloud.storage.VolumeVO; import com.cloud.user.Account; import com.cloud.user.AccountService; -import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -195,13 +197,6 @@ public class SnapshotJoinDaoImpl extends GenericDaoBaseWithTagInformation, Integer> searchIncludingRemovedAndCount(final SearchCriteria sc, final Filter filter) { - List objects = searchIncludingRemoved(sc, filter, null, false); - Integer count = getDistinctCount(sc); - return new Pair<>(objects, count); - } - @Override public List searchBySnapshotStorePair(String... pairs) { // set detail batch query size @@ -246,14 +241,33 @@ public class SnapshotJoinDaoImpl extends GenericDaoBaseWithTagInformation findById(Long zoneId, long id) { + SearchBuilder sb = createSearchBuilder(); + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("id", id); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + List 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 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 sc = snapshotIdsSearch.create(); if (zoneId != null) { sc.setParameters("zoneId", zoneId); diff --git a/server/src/test/java/com/cloud/api/query/dao/SnapshotJoinDaoImplTest.java b/server/src/test/java/com/cloud/api/query/dao/SnapshotJoinDaoImplTest.java new file mode 100644 index 00000000000..c1159d3aa88 --- /dev/null +++ b/server/src/test/java/com/cloud/api/query/dao/SnapshotJoinDaoImplTest.java @@ -0,0 +1,109 @@ +// 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.api.query.vo.SnapshotJoinVO; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class SnapshotJoinDaoImplTest { + + @Spy + @InjectMocks + SnapshotJoinDaoImpl snapshotJoinDao = new SnapshotJoinDaoImpl(); + + @Mock + SearchCriteria mockSearchCriteria; + + @Before + public void setUp() { + SnapshotJoinVO mockSnap = mock(SnapshotJoinVO.class); + SearchBuilder mockSearchBuilder = mock(SearchBuilder.class); + when(mockSearchBuilder.entity()).thenReturn(mockSnap); + doReturn(mockSearchBuilder).when(snapshotJoinDao).createSearchBuilder(); + when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria); + } + + @Test + public void testFindById_WithNullZoneId_EmptyResult() { + Long zoneId = null; + long id = 1L; + doReturn(Collections.emptyList()).when(snapshotJoinDao).search(mockSearchCriteria, null); + List result = snapshotJoinDao.findById(zoneId, id); + assertNull(result); + verify(mockSearchCriteria).setParameters("id", id); + verify(mockSearchCriteria, never()).setParameters("zoneId", zoneId); + } + + @Test + public void testFindById_WithValidZoneId_EmptyResult() { + Long zoneId = 1L; + long id = 1L; + doReturn(Collections.emptyList()).when(snapshotJoinDao).search(mockSearchCriteria, null); + List result = snapshotJoinDao.findById(zoneId, id); + assertNull(result); + verify(mockSearchCriteria).setParameters("id", id); + verify(mockSearchCriteria).setParameters("zoneId", zoneId); + } + + @Test + public void testFindById_WithValidResults() { + Long zoneId = 1L; + long id = 1L; + SnapshotJoinVO snapshot1 = mock(SnapshotJoinVO.class); + when(snapshot1.getSnapshotStorePair()).thenReturn("Primary_1"); + SnapshotJoinVO snapshot2 = mock(SnapshotJoinVO.class); + when(snapshot2.getSnapshotStorePair()).thenReturn("Image_1"); + doReturn(Arrays.asList(snapshot1, snapshot2)).when(snapshotJoinDao).search(mockSearchCriteria, null); + List result = snapshotJoinDao.findById(zoneId, id); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals("Image_1", result.get(0).getSnapshotStorePair()); + verify(mockSearchCriteria).setParameters("id", id); + verify(mockSearchCriteria).setParameters("zoneId", zoneId); + } + + @Test + public void testFindById_WithNullResults() { + long id = 1L; + doReturn(null).when(snapshotJoinDao).search(mockSearchCriteria, null); + List result = snapshotJoinDao.findById(null, id); + assertNull(result); + } +}