diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java index 28e9052124e..0fffefaee3f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java @@ -150,7 +150,7 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme protected String userData; @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the Userdata", since = "4.18") - private Long userdataId; + protected Long userdataId; @Parameter(name = ApiConstants.USER_DATA_DETAILS, type = CommandType.MAP, description = "used to specify the parameters values for the variables in userdata.", since = "4.18") private Map userdataDetails; @@ -200,7 +200,7 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme @ACL @Parameter(name = ApiConstants.AFFINITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups id that are going to be applied to the virtual machine." + " Mutually exclusive with affinitygroupnames parameter") - private List affinityGroupIdList; + protected List affinityGroupIdList; @ACL @Parameter(name = ApiConstants.AFFINITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups names that are going to be applied to the virtual machine." diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index f9401286192..13baf0fe4cc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -155,6 +155,14 @@ public class DeployVMCmd extends BaseDeployVMCmd { this.displayVm = displayVm; } + public void setUserDataId(Long userDataId) { + this.userdataId = userDataId; + } + + public void setAffinityGroupIds(List ids) { + this.affinityGroupIdList = ids; + } + public void setDetails(Map details) { this.details = details; } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java index 7952147490e..76509d2a6d1 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java @@ -23,6 +23,7 @@ import com.cloud.cpu.CPU; import com.cloud.dc.ClusterVO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; public interface ClusterDao extends GenericDao { @@ -62,5 +63,5 @@ public interface ClusterDao extends GenericDao { List listEnabledClusterIdsByZoneHypervisorArch(Long zoneId, HypervisorType hypervisorType, CPU.CPUArch arch); - List listByHypervisorType(HypervisorType hypervisorType); + List listByHypervisorType(HypervisorType hypervisorType, Filter filter); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java index 8988522fc96..1e36e0a780d 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java @@ -38,6 +38,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Grouping; import com.cloud.org.Managed; import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; @@ -415,9 +416,9 @@ public class ClusterDaoImpl extends GenericDaoBase implements C } @Override - public List listByHypervisorType(HypervisorType hypervisorType) { + public List listByHypervisorType(HypervisorType hypervisorType, Filter filter) { SearchCriteria sc = ZoneHyTypeSearch.create(); sc.setParameters("hypervisorType", hypervisorType.toString()); - return listBy(sc); + return listBy(sc, filter); } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java index 341f9d7cb84..243a9906486 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java @@ -24,6 +24,7 @@ import com.cloud.network.Network; import com.cloud.network.Network.GuestType; import com.cloud.network.Network.State; import com.cloud.network.Networks.TrafficType; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.fsm.StateDao; @@ -96,9 +97,11 @@ public interface NetworkDao extends GenericDao, StateDao serviceProviderMap); + List listByZoneAndTrafficType(long zoneId, TrafficType trafficType, Filter filter); + List listByZoneAndTrafficType(long zoneId, TrafficType trafficType); - List listByTrafficType(TrafficType trafficType); + List listByTrafficType(TrafficType trafficType, Filter filter); void setCheckForGc(long networkId); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java index 9a01a8ee7e3..218c447e3bc 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java @@ -632,20 +632,25 @@ public class NetworkDaoImpl extends GenericDaoBaseimplements Ne } @Override - public List listByZoneAndTrafficType(final long zoneId, final TrafficType trafficType) { + public List listByZoneAndTrafficType(final long zoneId, final TrafficType trafficType, Filter filter) { final SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("datacenter", zoneId); sc.setParameters("trafficType", trafficType); - return listBy(sc, null); + return listBy(sc, filter); } @Override - public List listByTrafficType(final TrafficType trafficType) { + public List listByZoneAndTrafficType(final long zoneId, final TrafficType trafficType) { + return listByZoneAndTrafficType(zoneId, trafficType, null); + } + + @Override + public List listByTrafficType(final TrafficType trafficType, Filter filter) { final SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("trafficType", trafficType); - return listBy(sc, null); + return listBy(sc, filter); } @Override diff --git a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java index 5f2225c410f..ccb6fea2059 100644 --- a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java +++ b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java @@ -23,6 +23,7 @@ import java.util.Set; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.tags.ResourceTagVO; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.api.response.ResourceTagResponse; @@ -61,5 +62,5 @@ public interface ResourceTagDao extends GenericDao { List listByResourceUuid(String resourceUuid); - List listByResourceType(ResourceObjectType resourceType); + List listByResourceType(ResourceObjectType resourceType, Filter filter); } diff --git a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java index 6fb7f71b269..091078f4628 100644 --- a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java @@ -28,6 +28,7 @@ import org.springframework.stereotype.Component; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.tags.ResourceTagVO; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -122,9 +123,9 @@ public class ResourceTagsDaoImpl extends GenericDaoBase imp } @Override - public List listByResourceType(ResourceObjectType resourceType) { + public List listByResourceType(ResourceObjectType resourceType, Filter filter) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("resourceType", resourceType); - return listBy(sc); + return listBy(sc, filter); } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/RouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/RouteHandler.java index 4e0381be699..d59ef9e2f79 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/RouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/RouteHandler.java @@ -19,7 +19,7 @@ package org.apache.cloudstack.veeam; import java.io.BufferedReader; import java.io.IOException; -import java.util.Map; +import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -30,6 +30,7 @@ import org.apache.logging.log4j.Logger; import com.cloud.utils.component.Adapter; public interface RouteHandler extends Adapter { + static final Pattern PAGE_PATTERN = Pattern.compile("\\bpage\\s+(\\d+)"); default int priority() { return 0; } boolean canHandle(String method, String path) throws IOException; void handle(HttpServletRequest req, HttpServletResponse resp, String path, Negotiation.OutFormat outFormat, VeeamControlServlet io) @@ -73,10 +74,4 @@ public interface RouteHandler extends Adapter { return null; } } - - static Map getRequestParams(HttpServletRequest req) { - return req.getParameterMap().entrySet().stream() - .filter(e -> e.getValue() != null && e.getValue().length > 0) - .collect(java.util.stream.Collectors.toMap(Map.Entry::getKey, e -> e.getValue()[0])); - } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ServerAdapter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ServerAdapter.java index a0eed5dbfc1..ae5eb6e0717 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ServerAdapter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ServerAdapter.java @@ -36,6 +36,9 @@ import org.apache.cloudstack.acl.RolePermissionEntity; import org.apache.cloudstack.acl.RoleService; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.Rule; +import org.apache.cloudstack.acl.SecurityChecker; +import org.apache.cloudstack.affinity.AffinityGroupVO; +import org.apache.cloudstack.affinity.dao.AffinityGroupDao; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiServerService; import org.apache.cloudstack.api.BaseCmd; @@ -116,20 +119,19 @@ import org.apache.cloudstack.veeam.api.dto.Vm; import org.apache.cloudstack.veeam.api.dto.VmAction; import org.apache.cloudstack.veeam.api.dto.VnicProfile; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import com.cloud.api.query.dao.AsyncJobJoinDao; import com.cloud.api.query.dao.DataCenterJoinDao; import com.cloud.api.query.dao.HostJoinDao; -import com.cloud.api.query.dao.ImageStoreJoinDao; import com.cloud.api.query.dao.StoragePoolJoinDao; import com.cloud.api.query.dao.UserVmJoinDao; import com.cloud.api.query.dao.VolumeJoinDao; import com.cloud.api.query.vo.AsyncJobJoinVO; import com.cloud.api.query.vo.DataCenterJoinVO; import com.cloud.api.query.vo.HostJoinVO; -import com.cloud.api.query.vo.ImageStoreJoinVO; import com.cloud.api.query.vo.StoragePoolJoinVO; import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.api.query.vo.VolumeJoinVO; @@ -152,6 +154,7 @@ import com.cloud.org.Grouping; import com.cloud.projects.Project; import com.cloud.projects.ProjectService; import com.cloud.server.ResourceTag; +import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; @@ -166,15 +169,18 @@ import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.user.User; import com.cloud.user.UserAccount; +import com.cloud.user.UserDataVO; +import com.cloud.user.dao.UserDataDao; import com.cloud.uservm.UserVm; import com.cloud.utils.EnumUtils; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.db.Filter; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.NicVO; -import com.cloud.vm.UserVmService; +import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; import com.cloud.vm.VmDetailConstants; import com.cloud.vm.dao.NicDao; @@ -183,8 +189,7 @@ import com.cloud.vm.dao.VMInstanceDetailsDao; import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; -// ToDo: fix list APIs to support pagination, etc -// ToDo: check access on objects +// ToDo: check access for list APIs when not ROOT admin public class ServerAdapter extends ManagerBase { private static final String SERVICE_ACCOUNT_NAME = "veemserviceuser"; @@ -206,7 +211,7 @@ public class ServerAdapter extends ManagerBase { ResizeVolumeCmd.class, ListNetworksCmd.class ); - public static final String GUEST_CPU_MODE = "host-passthrough"; + public static final String WORKER_VM_GUEST_CPU_MODE = "host-passthrough"; @Inject RoleService roleService; @@ -223,9 +228,6 @@ public class ServerAdapter extends ManagerBase { @Inject StoragePoolJoinDao storagePoolJoinDao; - @Inject - ImageStoreJoinDao imageStoreJoinDao; - @Inject ClusterDao clusterDao; @@ -275,7 +277,7 @@ public class ServerAdapter extends ManagerBase { VMTemplateDao templateDao; @Inject - UserVmService userVmService; + UserVmManager userVmManager; @Inject NicDao nicDao; @@ -304,6 +306,12 @@ public class ServerAdapter extends ManagerBase { @Inject ProjectService projectService; + @Inject + AffinityGroupDao affinityGroupDao; + + @Inject + UserDataDao userDataDao; + protected static Tag getDummyTagByName(String name) { Tag tag = new Tag(); String id = UUID.nameUUIDFromBytes(String.format("veeam:%s", name.toLowerCase()).getBytes()).toString(); @@ -429,15 +437,22 @@ public class ServerAdapter extends ManagerBase { waitForJobCompletion(job.getId()); } + protected void validateServiceAccountAdminAccess() { + Pair serviceAccount = getServiceAccount(); + if (!accountService.isAdmin(serviceAccount.second().getId())) { + throw new InvalidParameterValueException("Service account does not have access"); + } + } + @Override public boolean start() { getServiceAccount(); - //find public custom disk offering return true; } - public List listAllDataCenters() { - final List clusters = dataCenterJoinDao.listAll(); + public List listAllDataCenters(Long offset, Long limit) { + Filter filter = new Filter(DataCenterJoinVO.class, "id", true, offset, limit); + final List clusters = dataCenterJoinDao.listAll(filter); return DataCenterJoinVOToDataCenterConverter.toDCList(clusters); } @@ -449,81 +464,92 @@ public class ServerAdapter extends ManagerBase { return DataCenterJoinVOToDataCenterConverter.toDataCenter(vo); } - public List listStorageDomainsByDcId(final String uuid) { - final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(uuid); + public List listStorageDomainsByDcId(final String uuid, final Long offset, final Long limit) { + final DataCenterVO dataCenterVO = dataCenterDao.findByUuid(uuid); if (dataCenterVO == null) { throw new InvalidParameterValueException("DataCenter with ID " + uuid + " not found"); } - List storagePoolVOS = storagePoolJoinDao.listAll(); - List storageDomains = StoreVOToStorageDomainConverter.toStorageDomainListFromPools(storagePoolVOS); - List imageStoreJoinVOS = imageStoreJoinDao.listAll(); - storageDomains.addAll(StoreVOToStorageDomainConverter.toStorageDomainListFromStores(imageStoreJoinVOS)); - return storageDomains; + validateServiceAccountAdminAccess(); + Filter filter = new Filter(StoragePoolJoinVO.class, "id", true, offset, limit); + List storagePoolVOS = storagePoolJoinDao.listByZoneAndProvider(dataCenterVO.getId(), filter); + return StoreVOToStorageDomainConverter.toStorageDomainListFromPools(storagePoolVOS); } - public List listNetworksByDcId(final String uuid) { + public List listNetworksByDcId(final String uuid, final Long offset, final Long limit) { final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(uuid); if (dataCenterVO == null) { throw new InvalidParameterValueException("DataCenter with ID " + uuid + " not found"); } - List networks = networkDao.listByZoneAndTrafficType(dataCenterVO.getId(), Networks.TrafficType.Guest); + validateServiceAccountAdminAccess(); + Filter filter = new Filter(NetworkVO.class, "id", true, offset, limit); + List networks = networkDao.listByZoneAndTrafficType(dataCenterVO.getId(), Networks.TrafficType.Guest, filter); return NetworkVOToNetworkConverter.toNetworkList(networks, (dcId) -> dataCenterVO); } - public List listAllClusters() { - final List clusters = clusterDao.listByHypervisorType(Hypervisor.HypervisorType.KVM); + public List listAllClusters(Long offset, Long limit) { + validateServiceAccountAdminAccess(); + Filter filter = new Filter(ClusterVO.class, "id", true, offset, limit); + final List clusters = clusterDao.listByHypervisorType(Hypervisor.HypervisorType.KVM, filter); return ClusterVOToClusterConverter.toClusterList(clusters, this::getZoneById); } public Cluster getCluster(String uuid) { + validateServiceAccountAdminAccess(); final ClusterVO vo = clusterDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("Cluster with ID " + uuid + " not found"); } - return ClusterVOToClusterConverter.toCluster(vo, this::getZoneById); + return ClusterVOToClusterConverter.toCluster(vo, this::getZoneById); } - public List listAllHosts() { - final List hosts = hostJoinDao.listRoutingHostsByHypervisor(Hypervisor.HypervisorType.KVM); + public List listAllHosts(Long offset, Long limit) { + validateServiceAccountAdminAccess(); + Filter filter = new Filter(HostJoinVO.class, "id", true, offset, limit); + final List hosts = hostJoinDao.listRoutingHostsByHypervisor(Hypervisor.HypervisorType.KVM, filter); return HostJoinVOToHostConverter.toHostList(hosts); } public Host getHost(String uuid) { + validateServiceAccountAdminAccess(); final HostJoinVO vo = hostJoinDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("Host with ID " + uuid + " not found"); } - return HostJoinVOToHostConverter.toHost(vo); + return HostJoinVOToHostConverter.toHost(vo); } - public List listAllNetworks() { - final List networks = networkDao.listAll(); + public List listAllNetworks(Long offset, Long limit) { + Filter filter = new Filter(NetworkVO.class, "id", true, offset, limit); + final List networks = networkDao.listByTrafficType(Networks.TrafficType.Guest, filter); return NetworkVOToNetworkConverter.toNetworkList(networks, this::getZoneById); } public Network getNetwork(String uuid) { final NetworkVO vo = networkDao.findByUuid(uuid); if (vo == null) { - throw new InvalidParameterValueException("Host with ID " + uuid + " not found"); + throw new InvalidParameterValueException("Network with ID " + uuid + " not found"); } + accountService.checkAccess(getServiceAccount().second(), null, false, vo); return NetworkVOToNetworkConverter.toNetwork(vo, this::getZoneById); } - public List listAllVnicProfiles() { - final List networks = networkDao.listByTrafficType(Networks.TrafficType.Guest); + public List listAllVnicProfiles(Long offset, Long limit) { + Filter filter = new Filter(NetworkVO.class, "id", true, offset, limit); + final List networks = networkDao.listByTrafficType(Networks.TrafficType.Guest, filter); return NetworkVOToVnicProfileConverter.toVnicProfileList(networks, this::getZoneById); } public VnicProfile getVnicProfile(String uuid) { final NetworkVO vo = networkDao.findByUuid(uuid); if (vo == null) { - throw new InvalidParameterValueException("Host with ID " + uuid + " not found"); + throw new InvalidParameterValueException("Nic profile with ID " + uuid + " not found"); } return NetworkVOToVnicProfileConverter.toVnicProfile(vo, this::getZoneById); } - public List listAllInstances() { - List vms = userVmJoinDao.listByHypervisorType(Hypervisor.HypervisorType.KVM); + public List listAllInstances(Long offset, Long limit) { + Filter filter = new Filter(UserVmJoinVO.class, "id", true, offset, limit); + List vms = userVmJoinDao.listByHypervisorType(Hypervisor.HypervisorType.KVM, filter); return UserVmJoinVOToVmConverter.toVmList(vms, this::getHostById, this::getDetailsByInstanceId); } @@ -539,17 +565,24 @@ public class ServerAdapter extends ManagerBase { allContent); } - Ternary getVmOwner(Vm request) { + Account getOwnerForInstanceCreation(Vm request) { if (!VeeamControlService.InstanceRestoreAssignOwner.value()) { - return new Ternary<>(null, null, null); + return null; } String accountUuid = request.getAccountId(); if (StringUtils.isBlank(accountUuid)) { - return new Ternary<>(null, null, null); + return null; } Account account = accountService.getActiveAccountByUuid(accountUuid); if (account == null) { logger.warn("Account with ID {} not found, unable to determine owner for VM creation request", accountUuid); + return null; + } + return account; + } + + Ternary getOwnerDetailsForInstanceCreation(Account account) { + if (account == null) { return new Ternary<>(null, null, null); } String accountName = account.getAccountName(); @@ -576,7 +609,7 @@ public class ServerAdapter extends ManagerBase { throw new InvalidParameterValueException("Invalid name specified for the VM"); } String displayName = name; - name = name.replaceAll("_", "-"); + name = name.replace("_", "-"); Long zoneId = null; Long clusterId = null; if (request.getCluster() != null && StringUtils.isNotEmpty(request.getCluster().getId())) { @@ -589,6 +622,10 @@ public class ServerAdapter extends ManagerBase { if (zoneId == null) { throw new InvalidParameterValueException("Failed to determine datacenter for VM creation request"); } + DataCenterVO zone = dataCenterDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("DataCenter could not be determined for the request"); + } Integer cpu = null; try { cpu = Integer.valueOf(request.getCpu().getTopology().getSockets()); @@ -605,12 +642,14 @@ public class ServerAdapter extends ManagerBase { if (memory == null) { throw new InvalidParameterValueException("Memory must be specified"); } + int memoryMB = (int)(memory / (1024L * 1024L)); String userdata = null; if (request.getInitialization() != null) { userdata = request.getInitialization().getCustomScript(); } Pair bootOptions = Vm.Bios.retrieveBootOptions(request.getBios()); - Ternary owner = getVmOwner(request); + Account owner = getOwnerForInstanceCreation(request); + Ternary ownerDetails = getOwnerDetailsForInstanceCreation(owner); String serviceOfferingUuid = null; if (request.getCpuProfile() != null && StringUtils.isNotEmpty(request.getCpuProfile().getId())) { serviceOfferingUuid = request.getCpuProfile().getId(); @@ -620,29 +659,68 @@ public class ServerAdapter extends ManagerBase { templateUuid = request.getTemplate().getId(); } Pair serviceUserAccount = getServiceAccount(); - CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); + CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { - return createInstance(zoneId, clusterId, owner.first(), owner.second(), owner.third(), name, displayName, - serviceOfferingUuid, cpu, memory, templateUuid, userdata, bootOptions.first(), bootOptions.second()); + return createInstance(zone, clusterId, owner, ownerDetails.first(), ownerDetails.second(), + ownerDetails.third(), name, displayName, serviceOfferingUuid, cpu, memoryMB, templateUuid, + userdata, bootOptions.first(), bootOptions.second(), request.getAffinityGroupId(), + request.getUserDataId(), request.getDetails()); } finally { CallContext.unregister(); } } - protected ServiceOffering getServiceOfferingIdForVmCreation(String serviceOfferingUuid, long zoneId, int cpu, long memory) { - if (StringUtils.isNotBlank(serviceOfferingUuid)) { - ServiceOffering offering = serviceOfferingDao.findByUuid(serviceOfferingUuid); - if (offering != null && !offering.isCustomized()) { - // ToDo: check offering is available in the specified zone and matches the requested cpu/memory if it's not a custom offering - return offering; + protected ServiceOffering getServiceOfferingFromRequest(com.cloud.dc.DataCenter zone, Account account, + String uuid, int cpu, int memory) { + if (StringUtils.isBlank(uuid)) { + return null; + } + ServiceOfferingVO offering = serviceOfferingDao.findByUuid(uuid); + if (offering == null) { + logger.warn("Service offering with ID {} linked with the VM request not found", uuid); + return null; + } + try { + accountService.checkAccess(account, offering, zone); + } catch (PermissionDeniedException e) { + logger.warn("Service offering with ID {} linked with the VM request is not accessible for the account {}. Offering: {}, zone: {}", + uuid, account, offering, zone); + return null; + } + if (!offering.isCustomized() && (offering.getCpu() != cpu || offering.getRamSize() != memory)) { + logger.warn("Service offering with ID {} linked with the VM request has different CPU or memory than requested. Offering: {}, requested CPU: {}, requested memory: {}", + uuid, offering, cpu, memory); + return null; + } + if (offering.isCustomized()) { + Map params = Map.of( + VmDetailConstants.CPU_NUMBER, String.valueOf(cpu), + VmDetailConstants.MEMORY, String.valueOf(memory) + ); + try { + userVmManager.validateCustomParameters(offering, params); + offering.setCpu(cpu); + offering.setRamSize(memory); + } catch (InvalidParameterValueException e) { + logger.warn("Service offering with ID {} linked with the VM request is customized but does not support requested CPU or memory. Offering: {}, requested CPU: {}, requested memory: {}", + uuid, offering, cpu, memory); + return null; } } + return offering; + } + + protected ServiceOffering getServiceOfferingIdForVmCreation(com.cloud.dc.DataCenter zone, Account account, + String serviceOfferingUuid, int cpu, int memory) { + ServiceOffering offering = getServiceOfferingFromRequest(zone, account, serviceOfferingUuid, cpu, memory); + if (offering != null) { + return offering; + } ListServiceOfferingsCmd cmd = new ListServiceOfferingsCmd(); ComponentContext.inject(cmd); - cmd.setZoneId(zoneId); + cmd.setZoneId(zone.getId()); cmd.setCpuNumber(cpu); - Integer memoryMB = (int)(memory / (1024L * 1024L)); - cmd.setMemory(memoryMB); + cmd.setMemory(memory); ListResponse offerings = queryService.searchForServiceOfferings(cmd); if (offerings.getResponses().isEmpty()) { return null; @@ -651,7 +729,7 @@ public class ServerAdapter extends ManagerBase { return serviceOfferingDao.findByUuid(uuid); } - protected VMTemplateVO getTemplateForVmCreation(String templateUuid) { + protected VMTemplateVO getTemplateForInstanceCreation(String templateUuid) { if (StringUtils.isBlank(templateUuid)) { return null; } @@ -663,17 +741,20 @@ public class ServerAdapter extends ManagerBase { return template; } - protected Vm createInstance(Long zoneId, Long clusterId, Long domainId, String accountName, Long projectId, - String name, String displayName, String serviceOfferingUuid, int cpu, long memory, String templateUuid, - String userdata, ApiConstants.BootType bootType, ApiConstants.BootMode bootMode) { - ServiceOffering serviceOffering = getServiceOfferingIdForVmCreation(serviceOfferingUuid, zoneId, cpu, memory); + protected Vm createInstance(com.cloud.dc.DataCenter zone, Long clusterId, Account owner, Long domainId, + String accountName, Long projectId, String name, String displayName, String serviceOfferingUuid, + int cpu, int memory, String templateUuid, String userdata, ApiConstants.BootType bootType, + ApiConstants.BootMode bootMode, String affinityGroupId, String userDataId, Map details) { + Account account = owner != null ? owner : CallContext.current().getCallingAccount(); + ServiceOffering serviceOffering = getServiceOfferingIdForVmCreation(zone, account, serviceOfferingUuid, cpu, + memory); if (serviceOffering == null) { throw new CloudRuntimeException("No service offering found for VM creation with specified CPU and memory"); } DeployVMCmdByAdmin cmd = new DeployVMCmdByAdmin(); cmd.setHttpMethod(BaseCmd.HTTPMethod.POST.name()); ComponentContext.inject(cmd); - cmd.setZoneId(zoneId); + cmd.setZoneId(zone.getId()); cmd.setClusterId(clusterId); if (domainId != null && StringUtils.isNotEmpty(accountName)) { cmd.setDomainId(domainId); @@ -696,22 +777,39 @@ public class ServerAdapter extends ManagerBase { if (bootMode != null) { cmd.setBootMode(bootMode.toString()); } - VMTemplateVO template = getTemplateForVmCreation(templateUuid); + VMTemplateVO template = getTemplateForInstanceCreation(templateUuid); if (template != null) { cmd.setTemplateId(template.getId()); } - // ToDo: handle any other field? - // Handle custom offerings + if (StringUtils.isNotBlank(affinityGroupId)) { + AffinityGroupVO group = affinityGroupDao.findByUuid(affinityGroupId); + if (group == null) { + logger.warn("Failed to find affinity group with ID {} specified in Instance creation request, " + + "skipping affinity group assignment", affinityGroupId); + } else { + cmd.setAffinityGroupIds(List.of(group.getId())); + } + } + if (StringUtils.isNotBlank(userDataId)) { + UserDataVO userData = userDataDao.findByUuid(userDataId); + if (userData == null) { + logger.warn("Failed to find userdata with ID {} specified in Instance creation request, " + + "skipping userdata assignment", userDataId); + } else { + cmd.setUserDataId(userData.getId()); + } + } cmd.setHypervisor(Hypervisor.HypervisorType.KVM.name()); + Map instanceDetails = getDetailsForInstanceCreation(userdata, serviceOffering, details); + if (MapUtils.isNotEmpty(instanceDetails)) { + Map> map = new HashMap<>(); + map.put(0, details); + cmd.setDetails(map); + } cmd.setBlankInstance(true); - Map details = new HashMap<>(); - details.put(VmDetailConstants.GUEST_CPU_MODE, GUEST_CPU_MODE); - Map> map = new HashMap<>(); - map.put(0, details); - cmd.setDetails(map); try { - UserVm vm = userVmService.createVirtualMachine(cmd); - vm = userVmService.finalizeCreateVirtualMachine(vm.getId()); + UserVm vm = userVmManager.createVirtualMachine(cmd); + vm = userVmManager.finalizeCreateVirtualMachine(vm.getId()); UserVmJoinVO vo = userVmJoinDao.findById(vm.getId()); return UserVmJoinVOToVmConverter.toVm(vo, this::getHostById, this::getDetailsByInstanceId, this::listDiskAttachmentsByInstanceId, this::listNicsByInstance, false); @@ -720,6 +818,35 @@ public class ServerAdapter extends ManagerBase { } } + @NotNull + private static Map getDetailsForInstanceCreation(String userdata, ServiceOffering serviceOffering, + Map existingDetails) { + Map details = new HashMap<>(); + List detailsTobeSkipped = List.of( + ApiConstants.BootType.BIOS.toString(), + ApiConstants.BootType.UEFI.toString()); + if (MapUtils.isNotEmpty(existingDetails)) { + for (Map.Entry entry : existingDetails.entrySet()) { + if (detailsTobeSkipped.contains(entry.getKey())) { + continue; + } + details.put(entry.getKey(), entry.getValue()); + } + } + if (StringUtils.isNotEmpty(userdata)) { + // Assumption: Only worker VM will have userdata and it needs CPU mode + details.put(VmDetailConstants.GUEST_CPU_MODE, WORKER_VM_GUEST_CPU_MODE); + } + if (serviceOffering.isCustomized()) { + details.put(VmDetailConstants.CPU_NUMBER, String.valueOf(serviceOffering.getCpu())); + details.put(VmDetailConstants.MEMORY, String.valueOf(serviceOffering.getRamSize())); + if (serviceOffering.getSpeed() == null && !details.containsKey(VmDetailConstants.CPU_SPEED)) { + details.put(VmDetailConstants.CPU_SPEED, String.valueOf(1000)); + } + } + return details; + } + public Vm updateInstance(String uuid, Vm request) { logger.warn("Received request to update VM with ID {}. No action, returning existing VM data.", uuid); return getInstance(uuid, false, false, false); @@ -856,51 +983,27 @@ public class ServerAdapter extends ManagerBase { return volumeApiService.getVolumePhysicalSize(vo.getFormat(), vo.getPath(), vo.getChainInfo()); } - public List listAllDisks() { - List kvmVolumes = volumeJoinDao.listByHypervisor(Hypervisor.HypervisorType.KVM); + public List listAllDisks(Long offset, Long limit) { + Filter filter = new Filter(VolumeJoinVO.class, "id", true, offset, limit); + List kvmVolumes = volumeJoinDao.listByHypervisor(Hypervisor.HypervisorType.KVM, filter); return VolumeJoinVOToDiskConverter.toDiskList(kvmVolumes, this::getVolumePhysicalSize); } public Disk getDisk(String uuid) { - VolumeJoinVO vo = volumeJoinDao.findByUuid(uuid); + VolumeVO vo = volumeDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("Disk with ID " + uuid + " not found"); } - return VolumeJoinVOToDiskConverter.toDisk(vo, this::getVolumePhysicalSize); + accountService.checkAccess(getServiceAccount().second(), null, false, vo); + return VolumeJoinVOToDiskConverter.toDisk(volumeJoinDao.findByUuid(uuid), this::getVolumePhysicalSize); } public Disk copyDisk(String uuid) { throw new InvalidParameterValueException("Copy Disk with ID " + uuid + " not implemented"); -// VolumeVO vo = volumeDao.findByUuid(uuid); -// if (vo == null) { -// throw new InvalidParameterValueException("Disk with ID " + uuid + " not found"); -// } -// Pair serviceUserAccount = createServiceAccountIfNeeded(); -// CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); -// try { -// Volume volume = volumeApiService.copyVolume(vo.getId(), vo.getName() + "_copy", null, null); -// VolumeJoinVO copiedVolumeVO = volumeJoinDao.findById(volume.getId()); -// return VolumeJoinVOToDiskConverter.toDisk(copiedVolumeVO); -// } finally { -// CallContext.unregister(); -// } } public Disk reduceDisk(String uuid) { throw new InvalidParameterValueException("Reduce Disk with ID " + uuid + " not implemented"); -// VolumeVO vo = volumeDao.findByUuid(uuid); -// if (vo == null) { -// throw new InvalidParameterValueException("Disk with ID " + uuid + " not found"); -// } -// Pair serviceUserAccount = createServiceAccountIfNeeded(); -// CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); -// try { -// Volume volume = volumeApiService.reduceDisk(vo.getId(), vo.getName() + "_copy", null, null); -// VolumeJoinVO copiedVolumeVO = volumeJoinDao.findById(volume.getId()); -// return VolumeJoinVOToDiskConverter.toDisk(copiedVolumeVO); -// } finally { -// CallContext.unregister(); -// } } protected List listDiskAttachmentsByInstanceId(final long instanceId) { @@ -913,6 +1016,7 @@ public class ServerAdapter extends ManagerBase { if (vo == null) { throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); } + accountService.checkAccess(getServiceAccount().second(), null, false, vo); return listDiskAttachmentsByInstanceId(vo.getId()); } @@ -953,6 +1057,8 @@ public class ServerAdapter extends ManagerBase { if (vmVo == null) { throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found"); } + Pair serviceUserAccount = getServiceAccount(); + accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, vmVo); if (request == null || request.getDisk() == null || StringUtils.isEmpty(request.getDisk().getId())) { throw new InvalidParameterValueException("Request disk data is empty"); } @@ -960,7 +1066,7 @@ public class ServerAdapter extends ManagerBase { if (volumeVO == null) { throw new InvalidParameterValueException("Disk with ID " + request.getDisk().getId() + " not found"); } - Pair serviceUserAccount = getServiceAccount(); + accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, vmVo); if (vmVo.getAccountId() != volumeVO.getAccountId()) { if (VeeamControlService.InstanceRestoreAssignOwner.value()) { assignVolumeToAccount(volumeVO, vmVo.getAccountId(), serviceUserAccount); @@ -1013,18 +1119,7 @@ public class ServerAdapter extends ManagerBase { if (StringUtils.isBlank(sizeStr)) { throw new InvalidParameterValueException("Provisioned size must be specified"); } - long provisionedSizeInGb; - try { - provisionedSizeInGb = Long.parseLong(sizeStr); - } catch (NumberFormatException ex) { - throw new InvalidParameterValueException("Invalid provisioned size: " + sizeStr); - } - if (provisionedSizeInGb <= 0) { - throw new InvalidParameterValueException("Provisioned size must be greater than zero"); - } - // round-up provisionedSizeInGb to the next whole GB - long GB = 1024L * 1024L * 1024L; - provisionedSizeInGb = Math.max(1L, (provisionedSizeInGb + GB - 1) / GB); + long provisionedSizeInGb = getProvisionedSizeInGb(sizeStr); Long initialSize = null; if (StringUtils.isNotBlank(request.getInitialSize())) { try { @@ -1049,6 +1144,22 @@ public class ServerAdapter extends ManagerBase { } } + private static long getProvisionedSizeInGb(String sizeStr) { + long provisionedSizeInGb; + try { + provisionedSizeInGb = Long.parseLong(sizeStr); + } catch (NumberFormatException ex) { + throw new InvalidParameterValueException("Invalid provisioned size: " + sizeStr); + } + if (provisionedSizeInGb <= 0) { + throw new InvalidParameterValueException("Provisioned size must be greater than zero"); + } + // round-up provisionedSizeInGb to the next whole GB + long GB = 1024L * 1024L * 1024L; + provisionedSizeInGb = Math.max(1L, (provisionedSizeInGb + GB - 1) / GB); + return provisionedSizeInGb; + } + @NotNull private Disk createDisk(Account serviceAccount, StoragePoolVO pool, String name, Long diskOfferingId, long sizeInGb, Long initialSize) { Volume volume; @@ -1084,6 +1195,7 @@ public class ServerAdapter extends ManagerBase { if (vo == null) { throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); } + accountService.checkAccess(getServiceAccount().second(), null, false, vo); return listNicsByInstance(vo.getId(), vo.getUuid()); } @@ -1119,7 +1231,7 @@ public class ServerAdapter extends ManagerBase { cmd.setAccountName(account.getAccountName()); } cmd.setSkipNetwork(true); - userVmService.moveVmToUser(cmd); + userVmManager.moveVmToUser(cmd); } catch (ResourceAllocationException | CloudRuntimeException | ResourceUnavailableException | InsufficientCapacityException e) { logger.error("Failed to assign {} to {}: {}", vmVO, account, e.getMessage(), e); @@ -1133,6 +1245,8 @@ public class ServerAdapter extends ManagerBase { if (vmVo == null) { throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found"); } + Pair serviceUserAccount = getServiceAccount(); + accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, vmVo); if (request == null || request.getVnicProfile() == null || StringUtils.isEmpty(request.getVnicProfile().getId())) { throw new InvalidParameterValueException("Request nic data is empty"); } @@ -1140,7 +1254,7 @@ public class ServerAdapter extends ManagerBase { if (networkVO == null) { throw new InvalidParameterValueException("VNic profile " + request.getVnicProfile().getId() + " not found"); } - Pair serviceUserAccount = getServiceAccount(); + accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, networkVO); if (vmVo.getAccountId() != networkVO.getAccountId() && networkVO.getAccountId() != Account.ACCOUNT_ID_SYSTEM && VeeamControlService.InstanceRestoreAssignOwner.value() && @@ -1156,7 +1270,7 @@ public class ServerAdapter extends ManagerBase { if (request.getMac() != null && StringUtils.isNotBlank(request.getMac().getAddress())) { cmd.setMacAddress(request.getMac().getAddress()); } - userVmService.addNicToVirtualMachine(cmd); + userVmManager.addNicToVirtualMachine(cmd); NicVO nic = nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(networkVO.getId(), vmVo.getId()); if (nic == null) { throw new CloudRuntimeException("Failed to attach NIC to VM"); @@ -1167,8 +1281,9 @@ public class ServerAdapter extends ManagerBase { } } - public List listAllImageTransfers() { - List imageTransfers = imageTransferDao.listAll(); + public List listAllImageTransfers(Long offset, Long limit) { + Filter filter = new Filter(ImageTransferVO.class, "id", true, offset, limit); + List imageTransfers = imageTransferDao.listAll(filter); return ImageTransferVOToImageTransferConverter.toImageTransferList(imageTransfers, this::getHostById, this::getVolumeById); } @@ -1177,6 +1292,7 @@ public class ServerAdapter extends ManagerBase { if (vo == null) { throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found"); } + accountService.checkAccess(getServiceAccount().second(), null, false, vo); return ImageTransferVOToImageTransferConverter.toImageTransfer(vo, this::getHostById, this::getVolumeById); } @@ -1191,6 +1307,8 @@ public class ServerAdapter extends ManagerBase { if (volumeVO == null) { throw new InvalidParameterValueException("Disk with ID " + request.getDisk().getId() + " not found"); } + Pair serviceUserAccount = getServiceAccount(); + accountService.checkAccess(serviceUserAccount.second(), null, false, volumeVO); Direction direction = EnumUtils.fromString(Direction.class, request.getDirection()); if (direction == null) { throw new InvalidParameterValueException("Invalid or missing direction"); @@ -1204,7 +1322,7 @@ public class ServerAdapter extends ManagerBase { } backupId = backupVO.getId(); } - return createImageTransfer(backupId, volumeVO.getId(), direction, format); + return createImageTransfer(backupId, volumeVO.getId(), direction, format, serviceUserAccount); } public boolean cancelImageTransfer(String uuid) { @@ -1212,6 +1330,7 @@ public class ServerAdapter extends ManagerBase { if (vo == null) { throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found"); } + accountService.checkAccess(getServiceAccount().second(), SecurityChecker.AccessType.OperateEntry, false, vo); return kvmBackupExportService.cancelImageTransfer(vo.getId()); } @@ -1220,11 +1339,12 @@ public class ServerAdapter extends ManagerBase { if (vo == null) { throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found"); } + accountService.checkAccess(getServiceAccount().second(), SecurityChecker.AccessType.OperateEntry, false, vo); return kvmBackupExportService.finalizeImageTransfer(vo.getId()); } - private ImageTransfer createImageTransfer(Long backupId, Long volumeId, Direction direction, Format format) { - Pair serviceUserAccount = getServiceAccount(); + private ImageTransfer createImageTransfer(Long backupId, Long volumeId, Direction direction, Format format, + Pair serviceUserAccount) { CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { org.apache.cloudstack.backup.ImageTransfer imageTransfer = @@ -1268,7 +1388,7 @@ public class ServerAdapter extends ManagerBase { return vmInstanceDetailsDao.listDetailsKeyPairs(instanceId, true); } - public List listAllJobs() { + public List listPendingJobs() { Pair serviceUserAccount = getServiceAccount(); List jobIds = asyncJobDao.listPendingJobIdsForAccount(serviceUserAccount.second().getId()); List jobJoinVOs = asyncJobJoinDao.listByIds(jobIds); @@ -1280,6 +1400,7 @@ public class ServerAdapter extends ManagerBase { if (vo == null) { throw new InvalidParameterValueException("Job with ID " + uuid + " not found"); } + accountService.checkAccess(getServiceAccount().second(), null, false, vo); return AsyncJobJoinVOToJobConverter.toJob(vo); } @@ -1298,6 +1419,7 @@ public class ServerAdapter extends ManagerBase { throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found"); } Pair serviceUserAccount = getServiceAccount(); + accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, vmVo); CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { CreateVMSnapshotCmd cmd = new CreateVMSnapshotCmd(); @@ -1329,6 +1451,7 @@ public class ServerAdapter extends ManagerBase { if (vo == null) { throw new InvalidParameterValueException("Snapshot with ID " + uuid + " not found"); } + accountService.checkAccess(getServiceAccount().second(), null, false, vo); UserVmVO vm = userVmDao.findById(vo.getVmId()); return VmSnapshotVOToSnapshotConverter.toSnapshot(vo, vm.getUuid()); } @@ -1340,6 +1463,7 @@ public class ServerAdapter extends ManagerBase { throw new InvalidParameterValueException("Snapshot with ID " + uuid + " not found"); } Pair serviceUserAccount = getServiceAccount(); + accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, vo); CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { DeleteVMSnapshotCmd cmd = new DeleteVMSnapshotCmd(); @@ -1372,6 +1496,7 @@ public class ServerAdapter extends ManagerBase { throw new InvalidParameterValueException("Snapshot with ID " + uuid + " not found"); } Pair serviceUserAccount = getServiceAccount(); + accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, vo); CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { RevertToVMSnapshotCmd cmd = new RevertToVMSnapshotCmd(); @@ -1412,6 +1537,7 @@ public class ServerAdapter extends ManagerBase { throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found"); } Pair serviceUserAccount = getServiceAccount(); + accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, vmVo); CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { StartBackupCmd cmd = new StartBackupCmd(); @@ -1442,6 +1568,7 @@ public class ServerAdapter extends ManagerBase { if (vo == null) { throw new InvalidParameterValueException("Backup with ID " + uuid + " not found"); } + accountService.checkAccess(getServiceAccount().second(), null, false, vo); return BackupVOToBackupConverter.toBackup(vo, id -> userVmDao.findById(id), this::getHostById, this::getBackupDisks); } @@ -1461,6 +1588,7 @@ public class ServerAdapter extends ManagerBase { throw new InvalidParameterValueException("Backup with ID " + backupUuid + " not found"); } Pair serviceUserAccount = getServiceAccount(); + accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, backup); CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { FinalizeBackupCmd cmd = new FinalizeBackupCmd(); @@ -1495,6 +1623,7 @@ public class ServerAdapter extends ManagerBase { if (vo == null) { throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); } + accountService.checkAccess(getServiceAccount().second(), null, false, vo); Checkpoint checkpoint = UserVmVOToCheckpointConverter.toCheckpoint(vo); if (checkpoint == null) { return Collections.emptyList(); @@ -1507,6 +1636,7 @@ public class ServerAdapter extends ManagerBase { if (vo == null) { throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found"); } + accountService.checkAccess(getServiceAccount().second(), SecurityChecker.AccessType.OperateEntry, false, vo); if (!Objects.equals(vo.getActiveCheckpointId(), checkpointId)) { logger.warn("Checkpoint ID {} does not match active checkpoint for VM {}", checkpointId, vmUuid); return; @@ -1525,9 +1655,11 @@ public class ServerAdapter extends ManagerBase { } } - public List listAllTags() { + public List listAllTags(final Long offset, final Long limit) { List tags = new ArrayList<>(getDummyTags().values()); - List vmResourceTags = resourceTagDao.listByResourceType(ResourceTag.ResourceObjectType.UserVm); + Filter filter = new Filter(ResourceTagVO.class, "id", true, offset, limit); + List vmResourceTags = resourceTagDao.listByResourceType(ResourceTag.ResourceObjectType.UserVm, + filter); if (CollectionUtils.isNotEmpty(vmResourceTags)) { tags.addAll(ResourceTagVOToTagConverter.toTags(vmResourceTags)); } @@ -1541,6 +1673,7 @@ public class ServerAdapter extends ManagerBase { Tag tag = getDummyTags().get(uuid); if (tag == null) { ResourceTagVO resourceTagVO = resourceTagDao.findByUuid(uuid); + accountService.checkAccess(getServiceAccount().second(), null, false, resourceTagVO); if (resourceTagVO != null) { tag = ResourceTagVOToTagConverter.toTag(resourceTagVO); } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ApiService.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ApiService.java index c9024633680..d076604515a 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ApiService.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ApiService.java @@ -76,16 +76,12 @@ public class ApiService extends ManagerBase implements RouteHandler { add(links, basePath + "/clusters?search={query}", "clusters/search"); add(links, basePath + "/datacenters", "datacenters"); add(links, basePath + "/datacenters?search={query}", "datacenters/search"); - add(links, basePath + "/events", "events"); - add(links, basePath + "/events;from={event_id}?search={query}", "events/search"); add(links, basePath + "/hosts", "hosts"); add(links, basePath + "/hosts?search={query}", "hosts/search"); add(links, basePath + "/networks", "networks"); add(links, basePath + "/networks?search={query}", "networks/search"); add(links, basePath + "/storagedomains", "storagedomains"); add(links, basePath + "/storagedomains?search={query}", "storagedomains/search"); - add(links, basePath + "/templates", "templates"); - add(links, basePath + "/templates?search={query}", "templates/search"); add(links, basePath + "/vms", "vms"); add(links, basePath + "/vms?search={query}", "vms/search"); add(links, basePath + "/disks", "disks"); diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ClustersRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ClustersRouteHandler.java index c3ee3ab3cdd..f4107ff3735 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ClustersRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ClustersRouteHandler.java @@ -29,11 +29,13 @@ import org.apache.cloudstack.veeam.VeeamControlServlet; import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.Cluster; import org.apache.cloudstack.veeam.api.dto.NamedList; +import org.apache.cloudstack.veeam.api.request.ListQuery; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.utils.component.ManagerBase; public class ClustersRouteHandler extends ManagerBase implements RouteHandler { @@ -84,9 +86,14 @@ public class ClustersRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = serverAdapter.listAllClusters(); - NamedList response = NamedList.of("cluster", result); - io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + try { + ListQuery query = ListQuery.fromRequest(req); + final List result = serverAdapter.listAllClusters(query.getOffset(), query.getLimit()); + NamedList response = NamedList.of("cluster", result); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (PermissionDeniedException e) { + io.badRequest(resp, e.getMessage(), outFormat); + } } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, @@ -96,6 +103,8 @@ public class ClustersRouteHandler extends ManagerBase implements RouteHandler { io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } catch (InvalidParameterValueException e) { io.notFound(resp, e.getMessage(), outFormat); + } catch (PermissionDeniedException e) { + io.badRequest(resp, e.getMessage(), outFormat); } } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DataCentersRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DataCentersRouteHandler.java index bf8e2885251..4ff5add7d3d 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DataCentersRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DataCentersRouteHandler.java @@ -31,11 +31,13 @@ import org.apache.cloudstack.veeam.api.dto.DataCenter; import org.apache.cloudstack.veeam.api.dto.NamedList; import org.apache.cloudstack.veeam.api.dto.Network; import org.apache.cloudstack.veeam.api.dto.StorageDomain; +import org.apache.cloudstack.veeam.api.request.ListQuery; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.utils.component.ManagerBase; public class DataCentersRouteHandler extends ManagerBase implements RouteHandler { @@ -81,11 +83,11 @@ public class DataCentersRouteHandler extends ManagerBase implements RouteHandler } else if (idAndSubPath.size() == 2) { String subPath = idAndSubPath.get(1); if ("storagedomains".equals(subPath)) { - handleGetStorageDomainsByDcId(id, resp, outFormat, io); + handleGetStorageDomainsByDcId(id, req, resp, outFormat, io); return; } if ("networks".equals(subPath)) { - handleGetNetworksByDcId(id, resp, outFormat, io); + handleGetNetworksByDcId(id, req, resp, outFormat, io); return; } } @@ -96,7 +98,8 @@ public class DataCentersRouteHandler extends ManagerBase implements RouteHandler protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = serverAdapter.listAllDataCenters(); + ListQuery query = ListQuery.fromRequest(req); + final List result = serverAdapter.listAllDataCenters(query.getOffset(), query.getLimit()); NamedList response = NamedList.of("data_center", result); io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } @@ -111,25 +114,35 @@ public class DataCentersRouteHandler extends ManagerBase implements RouteHandler } } - protected void handleGetStorageDomainsByDcId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, - final VeeamControlServlet io) throws IOException { + protected void handleGetStorageDomainsByDcId(final String id, final HttpServletRequest req, + final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) + throws IOException { try { - List storageDomains = serverAdapter.listStorageDomainsByDcId(id); + ListQuery query = ListQuery.fromRequest(req); + List storageDomains = serverAdapter.listStorageDomainsByDcId(id, query.getPage(), + query.getMax()); NamedList response = NamedList.of("storage_domain", storageDomains); io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } catch (InvalidParameterValueException e) { io.notFound(resp, e.getMessage(), outFormat); + } catch (PermissionDeniedException e) { + io.badRequest(resp, e.getMessage(), outFormat); } } - protected void handleGetNetworksByDcId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, - final VeeamControlServlet io) throws IOException { + protected void handleGetNetworksByDcId(final String id, final HttpServletRequest req, + final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) + throws IOException { try { - List networks = serverAdapter.listNetworksByDcId(id); + ListQuery query = ListQuery.fromRequest(req); + List networks = serverAdapter.listNetworksByDcId(id, query.getPage(), + query.getMax()); NamedList response = NamedList.of("network", networks); io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } catch (InvalidParameterValueException e) { io.notFound(resp, e.getMessage(), outFormat); + } catch (PermissionDeniedException e) { + io.badRequest(resp, e.getMessage(), outFormat); } } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DisksRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DisksRouteHandler.java index f0fc1368d56..f4cd3b6a378 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DisksRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DisksRouteHandler.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.veeam.VeeamControlServlet; import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.Disk; import org.apache.cloudstack.veeam.api.dto.NamedList; +import org.apache.cloudstack.veeam.api.request.ListQuery; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; @@ -120,7 +121,8 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = serverAdapter.listAllDisks(); + ListQuery query = ListQuery.fromRequest(req); + final List result = serverAdapter.listAllDisks(query.getOffset(), query.getLimit()); NamedList response = NamedList.of("disk", result); io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/HostsRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/HostsRouteHandler.java index 54f19424cf9..931291714c6 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/HostsRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/HostsRouteHandler.java @@ -29,11 +29,13 @@ import org.apache.cloudstack.veeam.VeeamControlServlet; import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.Host; import org.apache.cloudstack.veeam.api.dto.NamedList; +import org.apache.cloudstack.veeam.api.request.ListQuery; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.utils.component.ManagerBase; public class HostsRouteHandler extends ManagerBase implements RouteHandler { @@ -84,9 +86,14 @@ public class HostsRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = serverAdapter.listAllHosts(); - NamedList response = NamedList.of("host", result); - io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + try { + ListQuery query = ListQuery.fromRequest(req); + final List result = serverAdapter.listAllHosts(query.getOffset(), query.getLimit()); + NamedList response = NamedList.of("host", result); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (PermissionDeniedException e) { + io.badRequest(resp, e.getMessage(), outFormat); + } } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, @@ -96,6 +103,8 @@ public class HostsRouteHandler extends ManagerBase implements RouteHandler { io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } catch (InvalidParameterValueException e) { io.notFound(resp, e.getMessage(), outFormat); + } catch (PermissionDeniedException e) { + io.badRequest(resp, e.getMessage(), outFormat); } } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ImageTransfersRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ImageTransfersRouteHandler.java index 33371bc3c35..00b473eb6a4 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ImageTransfersRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ImageTransfersRouteHandler.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.veeam.VeeamControlServlet; import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.ImageTransfer; import org.apache.cloudstack.veeam.api.dto.NamedList; +import org.apache.cloudstack.veeam.api.request.ListQuery; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; @@ -105,7 +106,8 @@ public class ImageTransfersRouteHandler extends ManagerBase implements RouteHand protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = serverAdapter.listAllImageTransfers(); + ListQuery query = ListQuery.fromRequest(req); + final List result = serverAdapter.listAllImageTransfers(query.getOffset(), query.getLimit()); NamedList response = NamedList.of("image_transfer", result); io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/JobsRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/JobsRouteHandler.java index a96c80aefe5..0cb03812769 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/JobsRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/JobsRouteHandler.java @@ -84,7 +84,7 @@ public class JobsRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = serverAdapter.listAllJobs(); + final List result = serverAdapter.listPendingJobs(); NamedList response = NamedList.of("job", result); io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/NetworksRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/NetworksRouteHandler.java index 5e5d9927e65..4014dc796fe 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/NetworksRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/NetworksRouteHandler.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.veeam.VeeamControlServlet; import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.NamedList; import org.apache.cloudstack.veeam.api.dto.Network; +import org.apache.cloudstack.veeam.api.request.ListQuery; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; @@ -84,7 +85,8 @@ public class NetworksRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = serverAdapter.listAllNetworks(); + ListQuery query = ListQuery.fromRequest(req); + final List result = serverAdapter.listAllNetworks(query.getOffset(), query.getLimit()); NamedList response = NamedList.of("network", result); io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/TagsRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/TagsRouteHandler.java index e81709cb212..b571bcaa2ed 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/TagsRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/TagsRouteHandler.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.veeam.VeeamControlServlet; import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.NamedList; import org.apache.cloudstack.veeam.api.dto.Tag; +import org.apache.cloudstack.veeam.api.request.ListQuery; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; @@ -85,7 +86,8 @@ public class TagsRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = serverAdapter.listAllTags(); + ListQuery query = ListQuery.fromRequest(req); + final List result = serverAdapter.listAllTags(query.getOffset(), query.getLimit()); NamedList response = NamedList.of("tag", result); io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VmsRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VmsRouteHandler.java index e911f7636de..fdf542d6471 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VmsRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VmsRouteHandler.java @@ -38,10 +38,7 @@ import org.apache.cloudstack.veeam.api.dto.ResourceAction; import org.apache.cloudstack.veeam.api.dto.Snapshot; import org.apache.cloudstack.veeam.api.dto.Vm; import org.apache.cloudstack.veeam.api.dto.VmAction; -import org.apache.cloudstack.veeam.api.request.VmListQuery; -import org.apache.cloudstack.veeam.api.request.VmSearchExpr; -import org.apache.cloudstack.veeam.api.request.VmSearchFilters; -import org.apache.cloudstack.veeam.api.request.VmSearchParser; +import org.apache.cloudstack.veeam.api.request.ListQuery; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; @@ -54,24 +51,10 @@ import com.fasterxml.jackson.core.JsonProcessingException; public class VmsRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/vms"; - private static final int DEFAULT_MAX = 50; - private static final int HARD_CAP_MAX = 1000; - private static final int DEFAULT_PAGE = 1; @Inject ServerAdapter serverAdapter; - private VmSearchParser searchParser; - - @Override - public boolean start() { - - this.searchParser = new VmSearchParser(Set.of( - "id", "name", "status", "cluster", "host", "template" - )); - return true; - } - @Override public int priority() { return 5; @@ -248,59 +231,12 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final VmListQuery q = fromRequest(req); - - // Validate max/page early (optional strictness) - if (q.getMax() != null && q.getMax() <= 0) { - io.notFound(resp, "Invalid 'max' (must be > 0)", outFormat); - return; - } - if (q.getPage() != null && q.getPage() <= 0) { - io.notFound(resp, "Invalid 'page' (must be > 0)", outFormat); - return; - } - - final int limit = q.resolvedMax(DEFAULT_MAX, HARD_CAP_MAX); - final int offset = q.offset(DEFAULT_MAX, HARD_CAP_MAX, DEFAULT_PAGE); - - final VmSearchExpr expr; - try { - expr = searchParser.parse(q.getSearch()); - } catch (VmSearchParser.VmSearchParseException e) { - io.notFound(resp, "Invalid search: " + e.getMessage(), outFormat); - return; - } - - final VmSearchFilters filters; - try { - filters = VmSearchFilters.fromAndOnly(expr); // AND-only v1 - } catch (VmSearchParser.VmSearchParseException e) { - io.notFound(resp, "Unsupported search: " + e.getMessage(), outFormat); - return; - } - - final List result = serverAdapter.listAllInstances(); + ListQuery query = ListQuery.fromRequest(req); + final List result = serverAdapter.listAllInstances(query.getOffset(), query.getLimit()); NamedList response = NamedList.of("vm", result); io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } - protected static VmListQuery fromRequest(final HttpServletRequest req) { - final VmListQuery q = new VmListQuery(); - q.setSearch(req.getParameter("search")); - q.setMax(parseIntOrNull(req.getParameter("max"))); - q.setPage(parseIntOrNull(req.getParameter("page"))); - return q; - } - - protected static Integer parseIntOrNull(final String s) { - if (s == null || s.trim().isEmpty()) return null; - try { - return Integer.parseInt(s.trim()); - } catch (NumberFormatException e) { - return Integer.valueOf(-1); // will be rejected by validation above - } - } - protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { String data = RouteHandler.getRequestData(req, logger); diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VnicProfilesRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VnicProfilesRouteHandler.java index 28f6b816d14..3e8aab2176f 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VnicProfilesRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VnicProfilesRouteHandler.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.veeam.VeeamControlServlet; import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.NamedList; import org.apache.cloudstack.veeam.api.dto.VnicProfile; +import org.apache.cloudstack.veeam.api.request.ListQuery; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; @@ -84,7 +85,8 @@ public class VnicProfilesRouteHandler extends ManagerBase implements RouteHandle protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = serverAdapter.listAllVnicProfiles(); + ListQuery query = ListQuery.fromRequest(req); + final List result = serverAdapter.listAllVnicProfiles(query.getOffset(), query.getLimit()); NamedList response = NamedList.of("vnic_profile", result); io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/HostJoinVOToHostConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/HostJoinVOToHostConverter.java index d627aa4d63f..4df1dd91e1c 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/HostJoinVOToHostConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/HostJoinVOToHostConverter.java @@ -73,7 +73,7 @@ public class HostJoinVOToHostConverter { // --- Memory --- h.setMemory(String.valueOf(vo.getTotalMemory())); - h.setMaxSchedulingMemory(String.valueOf(vo.getTotalMemory() - vo.getMemUsedCapacity())); // ToDo: check + h.setMaxSchedulingMemory(String.valueOf(vo.getTotalMemory() - vo.getMemUsedCapacity())); // --- OS / versions (optional placeholders) --- // If you want, you can set conservative defaults to match oVirt shape. diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/UserVmJoinVOToVmConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/UserVmJoinVOToVmConverter.java index 44691a0ef49..7f148b8d65b 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/UserVmJoinVOToVmConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/UserVmJoinVOToVmConverter.java @@ -129,8 +129,9 @@ public final class UserVmJoinVOToVmConverter { os.setBoot(boot); dst.setOs(os); Vm.Bios bios = Vm.Bios.getDefault(); + Map details = null; if (detailsResolver != null) { - Map details = detailsResolver.apply(src.getId()); + details = detailsResolver.apply(src.getId()); Vm.Bios.updateBios(bios, MapUtils.getString(details, ApiConstants.BootType.UEFI.toString())); } dst.setBios(bios); @@ -167,6 +168,11 @@ public final class UserVmJoinVOToVmConverter { dst.setInitialization(getOvfInitialization(dst, src)); } + dst.setAccountId(src.getAccountUuid()); + dst.setAffinityGroupId(src.getAffinityGroupUuid()); + dst.setUserDataId(src.getUserDataUuid()); + dst.setDetails(details); + return dst; } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/OvfXmlUtil.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/OvfXmlUtil.java index fcccf299f27..d417ffde17d 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/OvfXmlUtil.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/OvfXmlUtil.java @@ -21,8 +21,10 @@ import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.TimeZone; import java.util.UUID; @@ -36,6 +38,7 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.w3c.dom.Document; @@ -195,6 +198,22 @@ public class OvfXmlUtil { sb.append(""); } sb.append(""); + if (MapUtils.isNotEmpty(vm.getDetails())) { + sb.append("
"); + for (Map.Entry entry : vm.getDetails().entrySet()) { + sb.append(""); + sb.append("").append(escapeText(entry.getKey())).append(""); + sb.append("").append(escapeText(entry.getValue())).append(""); + sb.append(""); + } + sb.append("
"); + } + if (vo.getUserDataId() != null) { + sb.append("").append(escapeText(vo.getUserDataUuid())).append(""); + } + if (vo.getAffinityGroupId() != null) { + sb.append("").append(escapeText(vo.getAffinityGroupUuid())).append(""); + } sb.append(""); sb.append(""); } @@ -518,14 +537,35 @@ public class OvfXmlUtil { if (StringUtils.isNotBlank(serviceOfferingId)) { vm.setCpuProfile(Ref.of("", serviceOfferingId)); } - } - - private static String xpathString(XPath xpath, Document doc, String expression) { + String affinityGroupId = xpathString(xpath, metadataSection, ".//*[local-name()='AffinityGroupId']/text()"); + if (StringUtils.isNotBlank(affinityGroupId)) { + vm.setAffinityGroupId(affinityGroupId); + } + String userDataId = xpathString(xpath, metadataSection, ".//*[local-name()='UserDataId']/text()"); + if (StringUtils.isNotBlank(userDataId)) { + vm.setUserDataId(userDataId); + } + final Map details = new HashMap<>(); try { - String value = (String) xpath.evaluate(expression, doc, XPathConstants.STRING); - return StringUtils.isBlank(value) ? null : value.trim(); - } catch (XPathExpressionException e) { - return null; + NodeList detailNodes = (NodeList) xpath.evaluate( + ".//*[local-name()='Details']/*[local-name()='Detail']", + metadataSection, + XPathConstants.NODESET + ); + + for (int i = 0; i < detailNodes.getLength(); i++) { + Node detailNode = detailNodes.item(i); + String key = xpathString(xpath, detailNode, "./*[local-name()='Key']/text()"); + if (StringUtils.isBlank(key)) { + continue; + } + String value = xpathString(xpath, detailNode, "./*[local-name()='Value']/text()"); + details.put(key, defaultString(value)); + } + } catch (XPathExpressionException ignored) { + } + if (!details.isEmpty()) { + vm.setDetails(details); } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Vm.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Vm.java index ccf496db192..b939224d874 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Vm.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Vm.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.veeam.api.dto; import java.util.List; +import java.util.Map; import org.apache.cloudstack.api.ApiConstants; @@ -79,6 +80,9 @@ public final class Vm extends BaseDto { // CloudStack-specific fields private String accountId; + private String affinityGroupId; + private String userDataId; + private Map details; public String getName() { return name; @@ -297,6 +301,33 @@ public final class Vm extends BaseDto { this.accountId = accountId; } + @JsonIgnore + public String getAffinityGroupId() { + return affinityGroupId; + } + + public void setAffinityGroupId(String affinityGroupId) { + this.affinityGroupId = affinityGroupId; + } + + @JsonIgnore + public String getUserDataId() { + return userDataId; + } + + public void setUserDataId(String userDataId) { + this.userDataId = userDataId; + } + + @JsonIgnore + public Map getDetails() { + return details; + } + + public void setDetails(Map details) { + this.details = details; + } + @JsonInclude(JsonInclude.Include.NON_NULL) public static final class Bios { diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/request/ListQuery.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/request/ListQuery.java new file mode 100644 index 00000000000..8a21b595b77 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/request/ListQuery.java @@ -0,0 +1,141 @@ +// 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 org.apache.cloudstack.veeam.api.request; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +public class ListQuery { + boolean allContent; + Long max; + Long page; + Map search; + + public boolean isAllContent() { + return allContent; + } + + public void setAllContent(boolean allContent) { + this.allContent = allContent; + } + + public Long getMax() { + return max; + } + + public void setMax(Long max) { + this.max = max; + } + + public Map getSearch() { + return search; + } + + public void setSearch(Map search) { + this.search = search; + } + + public Long getPage() { + return page; + } + + public Long getOffset() { + if (page == null || max == null) { + return null; + } + return Math.max(0, (page - 1)) * max; + } + + public Long getLimit() { + return max; + } + + public static ListQuery fromRequest(HttpServletRequest request) { + ListQuery query = new ListQuery(); + if (MapUtils.isEmpty(request.getParameterMap())) { + return query; + } + + String allContent = request.getParameter("all_content"); + if (StringUtils.isNotBlank(allContent)) { + query.setAllContent(Boolean.parseBoolean(allContent)); + } + String max = request.getParameter("max"); + if (StringUtils.isNotBlank(max)) { + try { + query.setMax(Long.parseLong(max)); + } catch (NumberFormatException e) { + // Ignore invalid max and keep default null value. + } + } + Map searchItems = getSearchMap(request.getParameter("search")); + if (!searchItems.isEmpty()) { + try { + query.setMax(Long.parseLong(searchItems.get("page"))); + } catch (NumberFormatException e) { + // Ignore invalid page and keep default null value. + } + query.setSearch(searchItems); + } + + return query; + } + + // Parse search clause. Only keep items which use simple '=' operator, and ignore others. For example: + // name=myvm and status=up --> {name=myvm, status=up} + // name=myvm and status!=down --> {name=myvm} (ignore status!=down because it uses '!=' operator) + @NotNull + private static Map getSearchMap(String searchClause) { + Map searchItems = new LinkedHashMap<>(); + if (StringUtils.isBlank(searchClause)) { + return searchItems; + } + String[] terms = searchClause.trim().split("(?i)\\s+and\\s+"); + for (String term : terms) { + if (term == null) { + continue; + } + String trimmedTerm = term.trim(); + if (trimmedTerm.isEmpty()) { + continue; + } + + int eqIdx = trimmedTerm.indexOf('='); + if (eqIdx <= 0 || eqIdx != trimmedTerm.lastIndexOf('=')) { + continue; + } + char prev = trimmedTerm.charAt(eqIdx - 1); + if (prev == '!' || prev == '<' || prev == '>') { + continue; + } + + String key = trimmedTerm.substring(0, eqIdx).trim(); + String value = trimmedTerm.substring(eqIdx + 1).trim(); + if (!key.isEmpty() && !value.isEmpty()) { + searchItems.put(key, value); + } + } + return searchItems; + } +} diff --git a/server/src/main/java/com/cloud/api/query/dao/HostJoinDao.java b/server/src/main/java/com/cloud/api/query/dao/HostJoinDao.java index 005e324cd71..acce4b7426a 100644 --- a/server/src/main/java/com/cloud/api/query/dao/HostJoinDao.java +++ b/server/src/main/java/com/cloud/api/query/dao/HostJoinDao.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.response.HostResponse; import com.cloud.api.query.vo.HostJoinVO; import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; public interface HostJoinDao extends GenericDao { @@ -42,6 +43,6 @@ public interface HostJoinDao extends GenericDao { List findByClusterId(Long clusterId, Host.Type type); - List listRoutingHostsByHypervisor(Hypervisor.HypervisorType hypervisorType); + List listRoutingHostsByHypervisor(Hypervisor.HypervisorType hypervisorType, Filter filter); } diff --git a/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java index be3598f9cc2..6d3174d9432 100644 --- a/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java @@ -55,6 +55,7 @@ import com.cloud.host.dao.HostDetailsDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.StorageStats; import com.cloud.user.AccountManager; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -414,7 +415,7 @@ public class HostJoinDaoImpl extends GenericDaoBase implements } @Override - public List listRoutingHostsByHypervisor(Hypervisor.HypervisorType hypervisorType) { + public List listRoutingHostsByHypervisor(Hypervisor.HypervisorType hypervisorType, Filter filter) { SearchBuilder sb = createSearchBuilder(); sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ); sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); @@ -423,6 +424,6 @@ public class HostJoinDaoImpl extends GenericDaoBase implements SearchCriteria sc = sb.create(); sc.setParameters("type", Host.Type.Routing); sc.setParameters("hypervisorType", hypervisorType); - return listBy(sc); + return listBy(sc, filter); } } diff --git a/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDao.java b/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDao.java index bc19e089205..dc19d848193 100644 --- a/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDao.java +++ b/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDao.java @@ -23,6 +23,7 @@ import org.apache.cloudstack.api.response.StoragePoolResponse; import com.cloud.api.query.vo.StoragePoolJoinVO; import com.cloud.storage.StoragePool; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; @@ -44,4 +45,6 @@ public interface StoragePoolJoinDao extends GenericDao List findStoragePoolByScopeAndRuleTags(Long datacenterId, Long podId, Long clusterId, ScopeType scopeType, List tags); + List listByZoneAndProvider(long zoneId, Filter filter); + } diff --git a/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java index 8bfce47b120..35651f65794 100644 --- a/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java @@ -49,6 +49,7 @@ import com.cloud.storage.StorageStats; import com.cloud.storage.VolumeApiServiceImpl; import com.cloud.user.AccountManager; import com.cloud.utils.StringUtils; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -410,4 +411,13 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase listByZoneAndProvider(long zoneId, Filter filter) { + SearchBuilder sb = createSearchBuilder(); + sb.and("zoneId", sb.entity().getZoneId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("zoneId", zoneId); + return listBy(sc, filter); + } } diff --git a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDao.java b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDao.java index 351e367e8d0..55d65df7ffb 100644 --- a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDao.java +++ b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDao.java @@ -20,6 +20,7 @@ import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.hypervisor.Hypervisor; import com.cloud.user.Account; import com.cloud.uservm.UserVm; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.api.ApiConstants.VMDetails; @@ -51,5 +52,5 @@ public interface UserVmJoinDao extends GenericDao { List listLeaseInstancesExpiringInDays(int days); - List listByHypervisorType(Hypervisor.HypervisorType hypervisorType); + List listByHypervisorType(Hypervisor.HypervisorType hypervisorType, Filter filter); } diff --git a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index 39b2b9b9421..d243bb7a546 100644 --- a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -84,6 +84,7 @@ import com.cloud.user.UserStatisticsVO; import com.cloud.user.dao.UserDao; import com.cloud.user.dao.UserStatisticsDao; import com.cloud.uservm.UserVm; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; @@ -498,7 +499,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation listByHypervisorType(Hypervisor.HypervisorType hypervisorType) { + public List listByHypervisorType(Hypervisor.HypervisorType hypervisorType, Filter filter) { SearchBuilder sb = createSearchBuilder(); sb.and("hypervisorType", sb.entity().getHypervisorType(), Op.EQ); sb.done(); SearchCriteria sc = sb.create(); sc.setParameters("hypervisorType", hypervisorType); - return listBy(sc); + return listBy(sc, filter); } } diff --git a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDao.java b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDao.java index e61ad1d8e2d..c3b5859120f 100644 --- a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDao.java +++ b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDao.java @@ -24,6 +24,7 @@ import org.apache.cloudstack.api.response.VolumeResponse; import com.cloud.api.query.vo.VolumeJoinVO; import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.Volume; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; public interface VolumeJoinDao extends GenericDao { @@ -38,5 +39,5 @@ public interface VolumeJoinDao extends GenericDao { List listByInstanceId(long instanceId); - List listByHypervisor(Hypervisor.HypervisorType hypervisorType); + List listByHypervisor(Hypervisor.HypervisorType hypervisorType, Filter filter); } diff --git a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java index 0261398a232..20b6d69c591 100644 --- a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java @@ -43,6 +43,7 @@ import com.cloud.storage.Volume; import com.cloud.user.AccountManager; import com.cloud.user.VmDiskStatisticsVO; import com.cloud.user.dao.VmDiskStatisticsDao; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.vm.VirtualMachine; @@ -381,7 +382,7 @@ public class VolumeJoinDaoImpl extends GenericDaoBaseWithTagInformation listByHypervisor(Hypervisor.HypervisorType hypervisorType) { + public List listByHypervisor(Hypervisor.HypervisorType hypervisorType, Filter filter) { SearchBuilder sb = createSearchBuilder(); sb.and("vmType", sb.entity().getVmType(), SearchCriteria.Op.EQ); sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); @@ -389,7 +390,7 @@ public class VolumeJoinDaoImpl extends GenericDaoBaseWithTagInformation sc = sb.create(); sc.setParameters("vmType", VirtualMachine.Type.User); sc.setParameters("hypervisorType", hypervisorType); - return search(sc, null); + return search(sc, filter); } } diff --git a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java index 0b60d99adc2..94549878b9f 100644 --- a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java @@ -429,7 +429,7 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro private int jobStatus; @Column(name = "affinity_group_id") - private long affinityGroupId; + private Long affinityGroupId; @Column(name = "affinity_group_uuid") private String affinityGroupUuid; @@ -1012,7 +1012,7 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro return ip6Cidr; } - public long getAffinityGroupId() { + public Long getAffinityGroupId() { return affinityGroupId; } @@ -1057,7 +1057,7 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro return userDataId; } - public String getUserDataUUid() { + public String getUserDataUuid() { return userDataUuid; } diff --git a/server/src/main/java/org/apache/cloudstack/backup/KVMBackupExportServiceImpl.java b/server/src/main/java/org/apache/cloudstack/backup/KVMBackupExportServiceImpl.java index 4594ca6301f..419e80ea9ad 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/KVMBackupExportServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/KVMBackupExportServiceImpl.java @@ -42,6 +42,7 @@ import org.apache.cloudstack.api.response.CheckpointResponse; import org.apache.cloudstack.api.response.ImageTransferResponse; import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.backup.dao.ImageTransferDao; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.managed.context.ManagedContextTimerTask; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -67,6 +68,8 @@ import com.cloud.storage.VolumeStats; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDetailsDao; +import com.cloud.user.AccountService; +import com.cloud.user.User; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.exception.CloudRuntimeException; @@ -104,6 +107,9 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup @Inject private PrimaryDataStoreDao primaryDataStoreDao; + @Inject + AccountService accountService; + private Timer imageTransferTimer; private boolean isKVMBackupExportServiceSupported(Long zoneId) { @@ -493,8 +499,10 @@ public class KVMBackupExportServiceImpl extends ManagerBase implements KVMBackup @Override public ImageTransfer createImageTransfer(long volumeId, Long backupId, ImageTransfer.Direction direction, ImageTransfer.Format format) { + User callingUser = CallContext.current().getCallingUser(); ImageTransfer imageTransfer; VolumeVO volume = volumeDao.findById(volumeId); + accountService.checkAccess(callingUser, volume); if (volume == null) { throw new CloudRuntimeException("Volume not found with the specified Id"); diff --git a/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java b/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java index cf71d74498f..4c646b5264b 100644 --- a/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java +++ b/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java @@ -28,6 +28,7 @@ import com.cloud.network.dao.NetworkAccountVO; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; @@ -160,13 +161,18 @@ public class MockNetworkDaoImpl extends GenericDaoBase implemen return false; } + @Override + public List listByZoneAndTrafficType(final long zoneId, final TrafficType trafficType, Filter filter) { + return null; + } + @Override public List listByZoneAndTrafficType(final long zoneId, final TrafficType trafficType) { return null; } @Override - public List listByTrafficType(final TrafficType trafficType) { + public List listByTrafficType(final TrafficType trafficType, Filter filter) { return null; }