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 243a9906486..57b98335a28 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 @@ -101,7 +101,8 @@ public interface NetworkDao extends GenericDao, StateDao listByZoneAndTrafficType(long zoneId, TrafficType trafficType); - List listByTrafficType(TrafficType trafficType, Filter filter); + List listByTrafficTypeAndOwners(final TrafficType trafficType, List accountIds, + List domainIds, 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 218c447e3bc..926e293bc2f 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 @@ -31,6 +31,7 @@ import javax.persistence.TableGenerator; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.api.ApiConstants; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.network.Network; @@ -646,9 +647,22 @@ public class NetworkDaoImpl extends GenericDaoBaseimplements Ne } @Override - public List listByTrafficType(final TrafficType trafficType, Filter filter) { - final SearchCriteria sc = AllFieldsSearch.create(); + public List listByTrafficTypeAndOwners(final TrafficType trafficType, List accountIds, + List domainIds, Filter filter) { + SearchBuilder sb = createSearchBuilder(); + sb.and("trafficType", sb.entity().getTrafficType(), Op.EQ); + sb.and().op("account", sb.entity().getAccountId(), Op.IN); + sb.or("domain", sb.entity().getDomainId(), Op.IN); + sb.cp(); + sb.done(); + final SearchCriteria sc = sb.create(); sc.setParameters("trafficType", trafficType); + if (CollectionUtils.isNotEmpty(accountIds)) { + sc.setParameters("account", accountIds.toArray()); + } + if (CollectionUtils.isNotEmpty(domainIds)) { + sc.setParameters("domain", domainIds); + } return listBy(sc, filter); } 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 ccb6fea2059..3b946eba962 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 @@ -20,12 +20,13 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.cloudstack.api.response.ResourceTagResponse; + 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; public interface ResourceTagDao extends GenericDao { @@ -62,5 +63,6 @@ public interface ResourceTagDao extends GenericDao { List listByResourceUuid(String resourceUuid); - List listByResourceType(ResourceObjectType resourceType, Filter filter); + List listByResourceTypeAndOwners(ResourceObjectType resourceType, List accountIds, + List domainIds, 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 091078f4628..47556018de4 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 @@ -16,13 +16,14 @@ // under the License. package com.cloud.tags.dao; -import java.util.List; -import java.util.Set; -import java.util.Map; import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.apache.cloudstack.api.response.ResourceTagResponse; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.server.ResourceTag; @@ -123,9 +124,22 @@ public class ResourceTagsDaoImpl extends GenericDaoBase imp } @Override - public List listByResourceType(ResourceObjectType resourceType, Filter filter) { - SearchCriteria sc = AllFieldsSearch.create(); + public List listByResourceTypeAndOwners(ResourceObjectType resourceType, List accountIds, + List domainIds, Filter filter) { + SearchBuilder sb = createSearchBuilder(); + sb.and("resourceType", sb.entity().getResourceType(), Op.EQ); + sb.and().op("account", sb.entity().getAccountId(), SearchCriteria.Op.IN); + sb.or("domain", sb.entity().getDomainId(), SearchCriteria.Op.IN); + sb.cp(); + sb.done(); + final SearchCriteria sc = sb.create();; sc.setParameters("resourceType", resourceType); + if (CollectionUtils.isNotEmpty(accountIds)) { + sc.setParameters("account", accountIds.toArray()); + } + if (CollectionUtils.isNotEmpty(domainIds)) { + sc.setParameters("domain", domainIds); + } return listBy(sc, filter); } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDao.java index e71dffb22d5..fab28dbc342 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDao.java @@ -22,6 +22,7 @@ import java.util.List; import org.apache.cloudstack.backup.ImageTransfer; import org.apache.cloudstack.backup.ImageTransferVO; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; public interface ImageTransferDao extends GenericDao { @@ -30,4 +31,5 @@ public interface ImageTransferDao extends GenericDao { ImageTransferVO findByVolume(Long volumeId); ImageTransferVO findUnfinishedByVolume(Long volumeId); List listByPhaseAndDirection(ImageTransfer.Phase phase, ImageTransfer.Direction direction); + List listByOwners(List accountIds, List domainIds, Filter filter); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDaoImpl.java index 95741fa054d..85dd174c129 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/ImageTransferDaoImpl.java @@ -23,8 +23,10 @@ import javax.annotation.PostConstruct; import org.apache.cloudstack.backup.ImageTransfer; import org.apache.cloudstack.backup.ImageTransferVO; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -102,4 +104,22 @@ public class ImageTransferDaoImpl extends GenericDaoBase sc.setParameters("direction", direction); return listBy(sc); } + + @Override + public List listByOwners(List accountIds, List domainIds, Filter filter) { + SearchBuilder sb = createSearchBuilder(); + sb.and().op("account", sb.entity().getAccountId(), SearchCriteria.Op.IN); + sb.or("domain", sb.entity().getDomainId(), SearchCriteria.Op.IN); + sb.cp(); + sb.done(); + final SearchCriteria sc = sb.create(); + if (CollectionUtils.isNotEmpty(accountIds)) { + sc.setParameters("account", accountIds.toArray()); + } + if (CollectionUtils.isNotEmpty(domainIds)) { + sc.setParameters("domain", domainIds); + } + + return listBy(sc, filter); + } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ApiAccess.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ApiAccess.java new file mode 100644 index 00000000000..4bb6de06e47 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ApiAccess.java @@ -0,0 +1,31 @@ +// 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.adapter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.cloudstack.api.BaseCmd; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ApiAccess { + Class command(); +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ApiAccessInterceptor.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ApiAccessInterceptor.java new file mode 100644 index 00000000000..b0cd0cd3378 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ApiAccessInterceptor.java @@ -0,0 +1,68 @@ +// 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.adapter; + +import java.lang.reflect.Method; + +import javax.inject.Inject; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.User; +import com.cloud.utils.Pair; + +public class ApiAccessInterceptor implements MethodInterceptor { + @Inject + AccountManager accountManager; + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + Method m = invocation.getMethod(); + Object target = invocation.getThis(); + if (target == null) { + return invocation.proceed(); + } + + ApiAccess access = m.getAnnotation(ApiAccess.class); + if (access == null) { + m = target.getClass().getMethod(m.getName(), m.getParameterTypes()); + access = m.getAnnotation(ApiAccess.class); + } + if (access == null) { + return invocation.proceed(); + } + + ServerAdapter adapter = (ServerAdapter) target; + Pair serviceUserAccount = adapter.getServiceAccount(); + String apiName = BaseCmd.getCommandNameByClass(access.command()); + + accountManager.checkApiAccess(serviceUserAccount.second(), apiName); + + CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); + try { + return invocation.proceed(); + } finally { + CallContext.unregister(); + } + } +} 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 bc59d50a43a..bc3d1aeada2 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 @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import javax.inject.Inject; @@ -41,31 +42,47 @@ 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.BaseAsyncCmd; import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.command.admin.backup.CreateImageTransferCmd; import org.apache.cloudstack.api.command.admin.backup.DeleteVmCheckpointCmd; import org.apache.cloudstack.api.command.admin.backup.FinalizeBackupCmd; +import org.apache.cloudstack.api.command.admin.backup.FinalizeImageTransferCmd; +import org.apache.cloudstack.api.command.admin.backup.ListImageTransfersCmd; +import org.apache.cloudstack.api.command.admin.backup.ListVmCheckpointsCmd; import org.apache.cloudstack.api.command.admin.backup.StartBackupCmd; +import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd; +import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; +import org.apache.cloudstack.api.command.admin.storage.ListStoragePoolsCmd; import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; import org.apache.cloudstack.api.command.admin.vm.DeployVMCmdByAdmin; +import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd; +import org.apache.cloudstack.api.command.user.job.ListAsyncJobsCmd; import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd; +import org.apache.cloudstack.api.command.user.tag.ListTagsCmd; import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd; import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd; +import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; import org.apache.cloudstack.api.command.user.vm.ListVMsCmd; import org.apache.cloudstack.api.command.user.vm.StartVMCmd; import org.apache.cloudstack.api.command.user.vm.StopVMCmd; +import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; import org.apache.cloudstack.api.command.user.vmsnapshot.CreateVMSnapshotCmd; import org.apache.cloudstack.api.command.user.vmsnapshot.DeleteVMSnapshotCmd; +import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd; import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToVMSnapshotCmd; import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd; import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; import org.apache.cloudstack.api.command.user.volume.DeleteVolumeCmd; +import org.apache.cloudstack.api.command.user.volume.DestroyVolumeCmd; import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd; import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; +import org.apache.cloudstack.api.command.user.zone.ListZonesCmd; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.backup.BackupVO; @@ -139,6 +156,8 @@ import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.domain.Domain; +import com.cloud.domain.dao.DomainDao; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; @@ -152,10 +171,11 @@ import com.cloud.network.dao.NetworkVO; import com.cloud.offering.ServiceOffering; import com.cloud.org.Grouping; import com.cloud.projects.Project; -import com.cloud.projects.ProjectService; +import com.cloud.projects.ProjectManager; import com.cloud.server.ResourceTag; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeApiService; @@ -167,6 +187,7 @@ import com.cloud.tags.ResourceTagVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.user.Account; import com.cloud.user.AccountService; +import com.cloud.user.DomainService; import com.cloud.user.User; import com.cloud.user.UserAccount; import com.cloud.user.UserDataVO; @@ -211,6 +232,11 @@ public class ServerAdapter extends ManagerBase { ResizeVolumeCmd.class, ListNetworksCmd.class ); + private static final List SUPPORTED_STORAGE_TYPES = Arrays.asList( + Storage.StoragePoolType.Filesystem, + Storage.StoragePoolType.NetworkFilesystem, + Storage.StoragePoolType.SharedMountPoint + ); public static final String WORKER_VM_GUEST_CPU_MODE = "host-passthrough"; @Inject @@ -304,7 +330,7 @@ public class ServerAdapter extends ManagerBase { NetworkModel networkModel; @Inject - ProjectService projectService; + ProjectManager projectManager; @Inject AffinityGroupDao affinityGroupDao; @@ -312,6 +338,12 @@ public class ServerAdapter extends ManagerBase { @Inject UserDataDao userDataDao; + @Inject + DomainService domainService; + + @Inject + DomainDao domainDao; + protected static Tag getDummyTagByName(String name) { Tag tag = new Tag(); String id = UUID.nameUUIDFromBytes(String.format("veeam:%s", name.toLowerCase()).getBytes()).toString(); @@ -346,7 +378,7 @@ public class ServerAdapter extends ManagerBase { return role; } - public Role getServiceAccountRole() { + protected Role getServiceAccountRole() { List roles = roleService.findRolesByName(SERVICE_ACCOUNT_ROLE_NAME); if (CollectionUtils.isNotEmpty(roles)) { Role role = roles.get(0); @@ -437,135 +469,15 @@ 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"); - } + protected ApiServerService.AsyncCmdResult processAsyncCmdWithContext(BaseAsyncCmd cmd, Map params) + throws Exception { + final CallContext ctx = CallContext.current(); + final long callerUserId = ctx.getCallingUserId(); + final Account caller = ctx.getCallingAccount(); + return apiServerService.processAsyncCmd(cmd, params, ctx, callerUserId, caller); } - @Override - public boolean start() { - getServiceAccount(); - return true; - } - - 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); - } - - public DataCenter getDataCenter(String uuid) { - final DataCenterJoinVO vo = dataCenterJoinDao.findByUuid(uuid); - if (vo == null) { - throw new InvalidParameterValueException("DataCenter with ID " + uuid + " not found"); - } - return DataCenterJoinVOToDataCenterConverter.toDataCenter(vo); - } - - 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"); - } - 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, final Long offset, final Long limit) { - final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(uuid); - if (dataCenterVO == null) { - throw new InvalidParameterValueException("DataCenter with ID " + uuid + " not found"); - } - 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(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); - } - - 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); - } - - 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("Network with ID " + uuid + " not found"); - } - accountService.checkAccess(getServiceAccount().second(), null, false, vo); - return NetworkVOToNetworkConverter.toNetwork(vo, this::getZoneById); - } - - 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("Nic profile with ID " + uuid + " not found"); - } - return NetworkVOToVnicProfileConverter.toVnicProfile(vo, this::getZoneById); - } - - 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); - } - - public Vm getInstance(String uuid, boolean includeDisks, boolean includeNics, boolean allContent) { - UserVmJoinVO vo = userVmJoinDao.findByUuid(uuid); - if (vo == null) { - throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); - } - return UserVmJoinVOToVmConverter.toVm(vo, this::getHostById, - this::getDetailsByInstanceId, - includeDisks ? this::listDiskAttachmentsByInstanceId : null, - includeNics ? this::listNicsByInstance : null, - allContent); - } - - Account getOwnerForInstanceCreation(Vm request) { + protected Account getOwnerForInstanceCreation(Vm request) { if (!VeeamControlService.InstanceRestoreAssignOwner.value()) { return null; } @@ -581,14 +493,14 @@ public class ServerAdapter extends ManagerBase { return account; } - Ternary getOwnerDetailsForInstanceCreation(Account account) { + protected Ternary getOwnerDetailsForInstanceCreation(Account account) { if (account == null) { return new Ternary<>(null, null, null); } String accountName = account.getAccountName(); Long projectId = null; if (Account.Type.PROJECT.equals(account.getType())) { - Project project = projectService.findByProjectAccountId(account.getId()); + Project project = projectManager.findByProjectAccountId(account.getId()); if (project == null) { logger.warn("Project for {} not found, unable to determine owner for VM creation request", account); return new Ternary<>(null, null, null); @@ -599,6 +511,491 @@ public class ServerAdapter extends ManagerBase { return new Ternary<>(account.getDomainId(), accountName, projectId); } + protected Pair, String> getResourceOwnerFilters() { + final Account caller = CallContext.current().getCallingAccount(); + final Account.Type type = caller.getType(); + if (Account.Type.ADMIN.equals(type)) { + return new Pair<>(null, null); + } + List permittedAccountIds = null; + String domainPath = null; + if (Account.Type.DOMAIN_ADMIN.equals(type) || Account.Type.NORMAL.equals(type)) { + permittedAccountIds = projectManager.listPermittedProjectAccounts(caller.getId()); + permittedAccountIds.add(caller.getId()); + } + if (Account.Type.DOMAIN_ADMIN.equals(type)) { + Domain domain = domainService.getDomain(caller.getDomainId()); + if (domain == null) { + throw new InvalidParameterValueException("Invalid service account specified"); + } + domainPath = domain.getPath(); + } + if (Account.Type.PROJECT.equals(type)) { + Project project = projectManager.findByProjectAccountId(caller.getId()); + if (project == null) { + throw new InvalidParameterValueException("Invalid service account specified"); + } + permittedAccountIds = new ArrayList<>(); + permittedAccountIds.add(caller.getId()); + } + return new Pair<>(permittedAccountIds, domainPath); + } + + protected Pair, List> getResourceOwnerFiltersWithDomainIds() { + Pair, String> filters = getResourceOwnerFilters(); + if (StringUtils.isNotBlank(filters.second())) { + return new Pair<>(filters.first(), domainDao.getDomainChildrenIds(filters.second())); + } + return new Pair<>(filters.first(), null); + } + + protected ServiceOfferingVO 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) { + ServiceOfferingVO offering = getServiceOfferingFromRequest(zone, account, serviceOfferingUuid, cpu, memory); + if (offering != null) { + return offering; + } + ListServiceOfferingsCmd cmd = new ListServiceOfferingsCmd(); + ComponentContext.inject(cmd); + cmd.setZoneId(zone.getId()); + cmd.setCpuNumber(cpu); + cmd.setMemory(memory); + ListResponse offerings = queryService.searchForServiceOfferings(cmd); + if (offerings.getResponses().isEmpty()) { + return null; + } + String uuid = offerings.getResponses().get(0).getId(); + offering = serviceOfferingDao.findByUuid(uuid); + if (offering.isCustomized()) { + offering.setCpu(cpu); + offering.setRamSize(memory); + } + return offering; + } + + protected VMTemplateVO getTemplateForInstanceCreation(String templateUuid) { + if (StringUtils.isBlank(templateUuid)) { + return null; + } + VMTemplateVO template = templateDao.findByUuid(templateUuid); + if (template == null) { + logger.warn("Template with ID {} not found, VM will be created with default template", templateUuid); + return null; + } + return template; + } + + 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(zone.getId()); + cmd.setClusterId(clusterId); + if (domainId != null && StringUtils.isNotEmpty(accountName)) { + cmd.setDomainId(domainId); + cmd.setAccountName(accountName); + } + if (projectId != null) { + cmd.setProjectId(projectId); + } + cmd.setName(name); + if (displayName != null) { + cmd.setDisplayName(displayName); + } + cmd.setServiceOfferingId(serviceOffering.getId()); + if (StringUtils.isNotEmpty(userdata)) { + cmd.setUserData(Base64.getEncoder().encodeToString(userdata.getBytes(StandardCharsets.UTF_8))); + } + if (bootType != null) { + cmd.setBootType(bootType.toString()); + } + if (bootMode != null) { + cmd.setBootMode(bootMode.toString()); + } + VMTemplateVO template = getTemplateForInstanceCreation(templateUuid); + if (template != null) { + cmd.setTemplateId(template.getId()); + } + 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, instanceDetails); + cmd.setDetails(map); + } + cmd.setBlankInstance(true); + try { + 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); + } catch (InsufficientCapacityException | ResourceUnavailableException | ResourceAllocationException | CloudRuntimeException e) { + throw new CloudRuntimeException("Failed to create VM: " + e.getMessage(), e); + } + } + + @NotNull + protected 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; + } + + protected 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; + } + + protected Long getVolumePhysicalSize(VolumeJoinVO vo) { + return volumeApiService.getVolumePhysicalSize(vo.getFormat(), vo.getPath(), vo.getChainInfo()); + } + + @NotNull + protected Disk createDisk(Account serviceAccount, StoragePoolVO pool, String name, Long diskOfferingId, long sizeInGb, Long initialSize) { + Volume volume; + try { + volume = volumeApiService.allocVolume(serviceAccount.getId(), pool.getDataCenterId(), diskOfferingId, null, + null, name, sizeInGb, null, null, null, null); + } catch (ResourceAllocationException e) { + throw new CloudRuntimeException(e.getMessage(), e); + } + if (volume == null) { + throw new CloudRuntimeException("Failed to create volume"); + } + volume = volumeApiService.createVolume(volume.getId(), null, null, pool.getId(), true); + if (initialSize != null) { + volumeDetailsDao.addDetail(volume.getId(), ApiConstants.VIRTUAL_SIZE, String.valueOf(initialSize), true); + } + + // Implementation for creating a Disk resource + return VolumeJoinVOToDiskConverter.toDisk(volumeJoinDao.findById(volume.getId()), this::getVolumePhysicalSize); + } + + protected List listNicsByInstance(final long instanceId, final String instanceUuid) { + List nics = nicDao.listByVmId(instanceId); + return NicVOToNicConverter.toNicList(nics, instanceUuid, this::getNetworkById); + } + + protected List listNicsByInstance(final UserVmJoinVO vo) { + return listNicsByInstance(vo.getId(), vo.getUuid()); + } + + protected boolean accountCannotAccessNetwork(NetworkVO networkVO, long accountId) { + Account account = accountService.getActiveAccountById(accountId); + try { + networkModel.checkNetworkPermissions(account, networkVO); + return false; + } catch (CloudRuntimeException e) { + logger.debug("{} cannot access {}: {}", account, networkVO, e.getMessage()); + } + return true; + } + + protected void assignVmToAccount(UserVmVO vmVO, long accountId) { + Account account = accountService.getActiveAccountById(accountId); + if (account == null) { + throw new InvalidParameterValueException("Account with ID " + accountId + " not found"); + } + try { + AssignVMCmd cmd = new AssignVMCmd(); + ComponentContext.inject(cmd); + cmd.setVirtualMachineId(vmVO.getId()); + cmd.setDomainId(account.getDomainId()); + if (Account.Type.PROJECT.equals(account.getType())) { + Project project = projectManager.findByProjectAccountId(account.getId()); + if (project == null) { + throw new InvalidParameterValueException("Project for " + account + " not found"); + } + cmd.setProjectId(project.getId()); + } else { + cmd.setAccountName(account.getAccountName()); + } + cmd.setSkipNetwork(true); + userVmManager.moveVmToUser(cmd); + } catch (ResourceAllocationException | CloudRuntimeException | ResourceUnavailableException | + InsufficientCapacityException e) { + logger.error("Failed to assign {} to {}: {}", vmVO, account, e.getMessage(), e); + } + } + + protected ImageTransfer createImageTransfer(Long backupId, Long volumeId, Direction direction, Format format) { + org.apache.cloudstack.backup.ImageTransfer imageTransfer = + kvmBackupExportService.createImageTransfer(volumeId, backupId, direction, format); + ImageTransferVO imageTransferVO = imageTransferDao.findById(imageTransfer.getId()); + return ImageTransferVOToImageTransferConverter.toImageTransfer(imageTransferVO, this::getHostById, + this::getVolumeById); + } + + protected DataCenterJoinVO getZoneById(Long zoneId) { + if (zoneId == null) { + return null; + } + return dataCenterJoinDao.findById(zoneId); + } + + protected HostJoinVO getHostById(Long hostId) { + if (hostId == null) { + return null; + } + return hostJoinDao.findById(hostId); + } + + protected VolumeJoinVO getVolumeById(Long volumeId) { + if (volumeId == null) { + return null; + } + return volumeJoinDao.findById(volumeId); + } + + protected NetworkVO getNetworkById(Long networkId) { + if (networkId == null) { + return null; + } + return networkDao.findById(networkId); + } + + protected Map getDetailsByInstanceId(Long instanceId) { + return vmInstanceDetailsDao.listDetailsKeyPairs(instanceId, true); + } + + @Override + public boolean start() { + getServiceAccount(); + return true; + } + + @ApiAccess(command = ListZonesCmd.class) + 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); + } + + @ApiAccess(command = ListZonesCmd.class) + public DataCenter getDataCenter(String uuid) { + final DataCenterJoinVO vo = dataCenterJoinDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("DataCenter with ID " + uuid + " not found"); + } + return DataCenterJoinVOToDataCenterConverter.toDataCenter(vo); + } + + @ApiAccess(command = ListStoragePoolsCmd.class) + 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"); + } + Filter filter = new Filter(StoragePoolJoinVO.class, "id", true, offset, limit); + List storagePoolVOS = storagePoolJoinDao.listByZoneAndType(dataCenterVO.getId(), + SUPPORTED_STORAGE_TYPES, filter); + return StoreVOToStorageDomainConverter.toStorageDomainListFromPools(storagePoolVOS); + } + + @ApiAccess(command = ListNetworksCmd.class) + 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"); + } + 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); + } + + @ApiAccess(command = ListClustersCmd.class) + public List listAllClusters(Long offset, Long limit) { + 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); + } + + @ApiAccess(command = ListClustersCmd.class) + public Cluster getCluster(String uuid) { + final ClusterVO vo = clusterDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Cluster with ID " + uuid + " not found"); + } + return ClusterVOToClusterConverter.toCluster(vo, this::getZoneById); + } + + @ApiAccess(command = ListHostsCmd.class) + public List listAllHosts(Long offset, Long limit) { + Filter filter = new Filter(HostJoinVO.class, "id", true, offset, limit); + final List hosts = hostJoinDao.listRoutingHostsByHypervisor(Hypervisor.HypervisorType.KVM, filter); + return HostJoinVOToHostConverter.toHostList(hosts); + } + + @ApiAccess(command = ListHostsCmd.class) + public Host getHost(String uuid) { + final HostJoinVO vo = hostJoinDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Host with ID " + uuid + " not found"); + } + return HostJoinVOToHostConverter.toHost(vo); + } + + @ApiAccess(command = ListNetworksCmd.class) + public List listAllNetworks(Long offset, Long limit) { + Filter filter = new Filter(NetworkVO.class, "id", true, offset, limit); + Pair, List> ownerDetails = getResourceOwnerFiltersWithDomainIds(); + final List networks = networkDao.listByTrafficTypeAndOwners(Networks.TrafficType.Guest, + ownerDetails.first(), ownerDetails.second(), filter); + return NetworkVOToNetworkConverter.toNetworkList(networks, this::getZoneById); + } + + @ApiAccess(command = ListNetworksCmd.class) + public Network getNetwork(String uuid) { + final NetworkVO vo = networkDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Network with ID " + uuid + " not found"); + } + accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, vo); + return NetworkVOToNetworkConverter.toNetwork(vo, this::getZoneById); + } + + @ApiAccess(command = ListNetworksCmd.class) + public List listAllVnicProfiles(Long offset, Long limit) { + Filter filter = new Filter(NetworkVO.class, "id", true, offset, limit); + Pair, List> ownerDetails = getResourceOwnerFiltersWithDomainIds(); + final List networks = networkDao.listByTrafficTypeAndOwners(Networks.TrafficType.Guest, + ownerDetails.first(), ownerDetails.second(), filter); + return NetworkVOToVnicProfileConverter.toVnicProfileList(networks, this::getZoneById); + } + + @ApiAccess(command = ListNetworksCmd.class) + public VnicProfile getVnicProfile(String uuid) { + final NetworkVO vo = networkDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Nic profile with ID " + uuid + " not found"); + } + return NetworkVOToVnicProfileConverter.toVnicProfile(vo, this::getZoneById); + } + + @ApiAccess(command = ListVMsCmd.class) + public List listAllInstances(Long offset, Long limit) { + Filter filter = new Filter(UserVmJoinVO.class, "id", true, offset, limit); + Pair, String> ownerDetails = getResourceOwnerFilters(); + List vms = userVmJoinDao.listByHypervisorTypeAndOwners(Hypervisor.HypervisorType.KVM, + ownerDetails.first(), ownerDetails.second(), filter); + return UserVmJoinVOToVmConverter.toVmList(vms, this::getHostById, this::getDetailsByInstanceId); + } + + @ApiAccess(command = ListVMsCmd.class) + public Vm getInstance(String uuid, boolean includeDisks, boolean includeNics, boolean allContent) { + UserVmJoinVO vo = userVmJoinDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); + } + return UserVmJoinVOToVmConverter.toVm(vo, this::getHostById, + this::getDetailsByInstanceId, + includeDisks ? this::listDiskAttachmentsByInstanceId : null, + includeNics ? this::listNicsByInstance : null, + allContent); + } + + @ApiAccess(command = DeployVMCmd.class) public Vm createInstance(Vm request) { if (request == null) { throw new InvalidParameterValueException("Request disk data is empty"); @@ -658,212 +1055,24 @@ public class ServerAdapter extends ManagerBase { if (request.getTemplate() != null && StringUtils.isNotEmpty(request.getTemplate().getId())) { templateUuid = request.getTemplate().getId(); } - Pair serviceUserAccount = getServiceAccount(); - CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); - try { - 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 ServiceOfferingVO 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) { - ServiceOfferingVO offering = getServiceOfferingFromRequest(zone, account, serviceOfferingUuid, cpu, memory); - if (offering != null) { - return offering; - } - ListServiceOfferingsCmd cmd = new ListServiceOfferingsCmd(); - ComponentContext.inject(cmd); - cmd.setZoneId(zone.getId()); - cmd.setCpuNumber(cpu); - cmd.setMemory(memory); - ListResponse offerings = queryService.searchForServiceOfferings(cmd); - if (offerings.getResponses().isEmpty()) { - return null; - } - String uuid = offerings.getResponses().get(0).getId(); - offering = serviceOfferingDao.findByUuid(uuid); - if (offering.isCustomized()) { - offering.setCpu(cpu); - offering.setRamSize(memory); - } - return offering; - } - - protected VMTemplateVO getTemplateForInstanceCreation(String templateUuid) { - if (StringUtils.isBlank(templateUuid)) { - return null; - } - VMTemplateVO template = templateDao.findByUuid(templateUuid); - if (template == null) { - logger.warn("Template with ID {} not found, VM will be created with default template", templateUuid); - return null; - } - return template; - } - - 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(zone.getId()); - cmd.setClusterId(clusterId); - if (domainId != null && StringUtils.isNotEmpty(accountName)) { - cmd.setDomainId(domainId); - cmd.setAccountName(accountName); - } - if (projectId != null) { - cmd.setProjectId(projectId); - } - cmd.setName(name); - if (displayName != null) { - cmd.setDisplayName(displayName); - } - cmd.setServiceOfferingId(serviceOffering.getId()); - if (StringUtils.isNotEmpty(userdata)) { - cmd.setUserData(Base64.getEncoder().encodeToString(userdata.getBytes(StandardCharsets.UTF_8))); - } - if (bootType != null) { - cmd.setBootType(bootType.toString()); - } - if (bootMode != null) { - cmd.setBootMode(bootMode.toString()); - } - VMTemplateVO template = getTemplateForInstanceCreation(templateUuid); - if (template != null) { - cmd.setTemplateId(template.getId()); - } - 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, instanceDetails); - cmd.setDetails(map); - } - cmd.setBlankInstance(true); - try { - 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); - } catch (InsufficientCapacityException | ResourceUnavailableException | ResourceAllocationException | CloudRuntimeException e) { - throw new CloudRuntimeException("Failed to create VM: " + e.getMessage(), e); - } - } - - @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; + 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()); } + @ApiAccess(command = UpdateVMCmd.class) 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); } + @ApiAccess(command = DestroyVMCmd.class) public VmAction deleteInstance(String uuid, boolean async) { UserVmVO vo = userVmDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); } - Pair serviceUserAccount = getServiceAccount(); - CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { DestroyVMCmd cmd = new DestroyVMCmd(); cmd.setHttpMethod(BaseCmd.HTTPMethod.POST.name()); @@ -871,9 +1080,7 @@ public class ServerAdapter extends ManagerBase { Map params = new HashMap<>(); params.put(ApiConstants.ID, vo.getUuid()); params.put(ApiConstants.EXPUNGE, Boolean.TRUE.toString()); - ApiServerService.AsyncCmdResult result = - apiServerService.processAsyncCmd(cmd, params, ctx, serviceUserAccount.first().getId(), - serviceUserAccount.second()); + ApiServerService.AsyncCmdResult result = processAsyncCmdWithContext(cmd, params); AsyncJobJoinVO jobVo = asyncJobJoinDao.findById(result.jobId); if (jobVo == null) { throw new CloudRuntimeException("Failed to find job for VM deletion"); @@ -884,27 +1091,22 @@ public class ServerAdapter extends ManagerBase { return AsyncJobJoinVOToJobConverter.toVmAction(jobVo, userVmJoinDao.findById(vo.getId())); } catch (Exception e) { throw new CloudRuntimeException("Failed to delete VM: " + e.getMessage(), e); - } finally { - CallContext.unregister(); } } + @ApiAccess(command = StartVMCmd.class) public VmAction startInstance(String uuid, boolean async) { UserVmVO vo = userVmDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); } - Pair serviceUserAccount = getServiceAccount(); - CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { StartVMCmd cmd = new StartVMCmd(); cmd.setHttpMethod(BaseCmd.HTTPMethod.POST.name()); ComponentContext.inject(cmd); Map params = new HashMap<>(); params.put(ApiConstants.ID, vo.getUuid()); - ApiServerService.AsyncCmdResult result = - apiServerService.processAsyncCmd(cmd, params, ctx, serviceUserAccount.first().getId(), - serviceUserAccount.second()); + ApiServerService.AsyncCmdResult result = processAsyncCmdWithContext(cmd, params); AsyncJobJoinVO jobVo = asyncJobJoinDao.findById(result.jobId); if (jobVo == null) { throw new CloudRuntimeException("Failed to find job for VM start"); @@ -915,18 +1117,15 @@ public class ServerAdapter extends ManagerBase { return AsyncJobJoinVOToJobConverter.toVmAction(jobVo, userVmJoinDao.findById(vo.getId())); } catch (Exception e) { throw new CloudRuntimeException("Failed to start VM: " + e.getMessage(), e); - } finally { - CallContext.unregister(); } } + @ApiAccess(command = StopVMCmd.class) public VmAction stopInstance(String uuid, boolean async) { UserVmVO vo = userVmDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); } - Pair serviceUserAccount = getServiceAccount(); - CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { StopVMCmd cmd = new StopVMCmd(); cmd.setHttpMethod(BaseCmd.HTTPMethod.POST.name()); @@ -934,9 +1133,7 @@ public class ServerAdapter extends ManagerBase { Map params = new HashMap<>(); params.put(ApiConstants.ID, vo.getUuid()); params.put(ApiConstants.FORCED, Boolean.TRUE.toString()); - ApiServerService.AsyncCmdResult result = - apiServerService.processAsyncCmd(cmd, params, ctx, serviceUserAccount.first().getId(), - serviceUserAccount.second()); + ApiServerService.AsyncCmdResult result = processAsyncCmdWithContext(cmd, params); AsyncJobJoinVO jobVo = asyncJobJoinDao.findById(result.jobId); if (jobVo == null) { throw new CloudRuntimeException("Failed to find job for VM stop"); @@ -947,18 +1144,15 @@ public class ServerAdapter extends ManagerBase { return AsyncJobJoinVOToJobConverter.toVmAction(jobVo, userVmJoinDao.findById(vo.getId())); } catch (Exception e) { throw new CloudRuntimeException("Failed to stop VM: " + e.getMessage(), e); - } finally { - CallContext.unregister(); } } + @ApiAccess(command = StopVMCmd.class) public VmAction shutdownInstance(String uuid, boolean async) { UserVmVO vo = userVmDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); } - Pair serviceUserAccount = getServiceAccount(); - CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { StopVMCmd cmd = new StopVMCmd(); cmd.setHttpMethod(BaseCmd.HTTPMethod.POST.name()); @@ -966,9 +1160,7 @@ public class ServerAdapter extends ManagerBase { Map params = new HashMap<>(); params.put(ApiConstants.ID, vo.getUuid()); params.put(ApiConstants.FORCED, Boolean.FALSE.toString()); - ApiServerService.AsyncCmdResult result = - apiServerService.processAsyncCmd(cmd, params, ctx, serviceUserAccount.first().getId(), - serviceUserAccount.second()); + ApiServerService.AsyncCmdResult result = processAsyncCmdWithContext(cmd, params); AsyncJobJoinVO jobVo = asyncJobJoinDao.findById(result.jobId); if (jobVo == null) { throw new CloudRuntimeException("Failed to find job for VM shutdown"); @@ -979,27 +1171,25 @@ public class ServerAdapter extends ManagerBase { return AsyncJobJoinVOToJobConverter.toVmAction(jobVo, userVmJoinDao.findById(vo.getId())); } catch (Exception e) { throw new CloudRuntimeException("Failed to shutdown VM: " + e.getMessage(), e); - } finally { - CallContext.unregister(); } } - protected Long getVolumePhysicalSize(VolumeJoinVO vo) { - return volumeApiService.getVolumePhysicalSize(vo.getFormat(), vo.getPath(), vo.getChainInfo()); - } - + @ApiAccess(command = ListVolumesCmd.class) 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); + Pair, String> ownerDetails = getResourceOwnerFilters(); + List kvmVolumes = volumeJoinDao.listByHypervisorTypeAndOwners(Hypervisor.HypervisorType.KVM, + ownerDetails.first(), ownerDetails.second(), filter); return VolumeJoinVOToDiskConverter.toDiskList(kvmVolumes, this::getVolumePhysicalSize); } + @ApiAccess(command = ListVolumesCmd.class) public Disk getDisk(String uuid) { VolumeVO vo = volumeDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("Disk with ID " + uuid + " not found"); } - accountService.checkAccess(getServiceAccount().second(), null, false, vo); + accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, vo); return VolumeJoinVOToDiskConverter.toDisk(volumeJoinDao.findByUuid(uuid), this::getVolumePhysicalSize); } @@ -1011,26 +1201,27 @@ public class ServerAdapter extends ManagerBase { throw new InvalidParameterValueException("Reduce Disk with ID " + uuid + " not implemented"); } + @ApiAccess(command = ListVolumesCmd.class) protected List listDiskAttachmentsByInstanceId(final long instanceId) { List kvmVolumes = volumeJoinDao.listByInstanceId(instanceId); return VolumeJoinVOToDiskConverter.toDiskAttachmentList(kvmVolumes, this::getVolumePhysicalSize); } + @ApiAccess(command = ListVolumesCmd.class) public List listDiskAttachmentsByInstanceUuid(final String uuid) { UserVmVO vo = userVmDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); } - accountService.checkAccess(getServiceAccount().second(), null, false, vo); + accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, vo); return listDiskAttachmentsByInstanceId(vo.getId()); } - protected void assignVolumeToAccount(VolumeVO volumeVO, long accountId, Pair serviceUserAccount) { + protected void assignVolumeToAccount(VolumeVO volumeVO, long accountId) { Account account = accountService.getActiveAccountById(accountId); if (account == null) { throw new InvalidParameterValueException("Account with ID " + accountId + " not found"); } - CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { AssignVolumeCmd cmd = new AssignVolumeCmd(); ComponentContext.inject(cmd); @@ -1038,7 +1229,7 @@ public class ServerAdapter extends ManagerBase { cmd.setVolumeId(volumeVO.getId()); params.put(ApiConstants.VOLUME_ID, volumeVO.getUuid()); if (Account.Type.PROJECT.equals(account.getType())) { - Project project = projectService.findByProjectAccountId(account.getId()); + Project project = projectManager.findByProjectAccountId(account.getId()); if (project == null) { throw new InvalidParameterValueException("Project for " + account + " not found"); } @@ -1052,18 +1243,17 @@ public class ServerAdapter extends ManagerBase { volumeApiService.assignVolumeToAccount(cmd); } catch (ResourceAllocationException | CloudRuntimeException e) { logger.error("Failed to assign {} to {}: {}", volumeVO, account, e.getMessage(), e); - } finally { - CallContext.unregister(); } } + @ApiAccess(command = AttachVolumeCmd.class) public DiskAttachment attachInstanceDisk(final String vmUuid, final DiskAttachment request) { UserVmVO vmVo = userVmDao.findByUuid(vmUuid); if (vmVo == null) { throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found"); } - Pair serviceUserAccount = getServiceAccount(); - accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, vmVo); + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, + false, vmVo); if (request == null || request.getDisk() == null || StringUtils.isEmpty(request.getDisk().getId())) { throw new InvalidParameterValueException("Request disk data is empty"); } @@ -1071,30 +1261,27 @@ public class ServerAdapter extends ManagerBase { if (volumeVO == null) { throw new InvalidParameterValueException("Disk with ID " + request.getDisk().getId() + " not found"); } - accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, vmVo); + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, + false, vmVo); if (vmVo.getAccountId() != volumeVO.getAccountId()) { if (VeeamControlService.InstanceRestoreAssignOwner.value()) { - assignVolumeToAccount(volumeVO, vmVo.getAccountId(), serviceUserAccount); + assignVolumeToAccount(volumeVO, vmVo.getAccountId()); } else { throw new PermissionDeniedException("Disk with ID " + request.getDisk().getId() + " belongs to a different account and cannot be attached to the VM"); } } - CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); - try { - Long deviceId = null; - List volumes = volumeDao.findUsableVolumesForInstance(vmVo.getId()); - if (CollectionUtils.isEmpty(volumes)) { - deviceId = 0L; - } - Volume volume = volumeApiService.attachVolumeToVM(vmVo.getId(), volumeVO.getId(), deviceId, false); - VolumeJoinVO attachedVolumeVO = volumeJoinDao.findById(volume.getId()); - return VolumeJoinVOToDiskConverter.toDiskAttachment(attachedVolumeVO, this::getVolumePhysicalSize); - } finally { - CallContext.unregister(); + Long deviceId = null; + List volumes = volumeDao.findUsableVolumesForInstance(vmVo.getId()); + if (CollectionUtils.isEmpty(volumes)) { + deviceId = 0L; } + Volume volume = volumeApiService.attachVolumeToVM(vmVo.getId(), volumeVO.getId(), deviceId, false); + VolumeJoinVO attachedVolumeVO = volumeJoinDao.findById(volume.getId()); + return VolumeJoinVOToDiskConverter.toDiskAttachment(attachedVolumeVO, this::getVolumePhysicalSize); } + @ApiAccess(command = DestroyVolumeCmd.class) public void deleteDisk(String uuid) { VolumeVO vo = volumeDao.findByUuid(uuid); if (vo == null) { @@ -1103,6 +1290,7 @@ public class ServerAdapter extends ManagerBase { volumeApiService.deleteVolume(vo.getId(), accountService.getSystemAccount()); } + @ApiAccess(command = CreateVolumeCmd.class) public Disk createDisk(Disk request) { if (request == null) { throw new InvalidParameterValueException("Request disk data is empty"); @@ -1131,127 +1319,36 @@ public class ServerAdapter extends ManagerBase { initialSize = Long.parseLong(request.getInitialSize()); } catch (NumberFormatException ignored) {} } - Pair serviceUserAccount = getServiceAccount(); - Account serviceAccount = serviceUserAccount.second(); + Account caller = CallContext.current().getCallingAccount(); DataCenterVO zone = dataCenterDao.findById(pool.getDataCenterId()); if (zone == null || !Grouping.AllocationState.Enabled.equals(zone.getAllocationState())) { throw new InvalidParameterValueException("Datacenter for the specified storage domain is not found or not active"); } - Long diskOfferingId = volumeApiService.getCustomDiskOfferingIdForVolumeUpload(serviceAccount, zone); + Long diskOfferingId = volumeApiService.getCustomDiskOfferingIdForVolumeUpload(caller, zone); if (diskOfferingId == null) { throw new CloudRuntimeException("Failed to find custom offering for disk" + zone.getName()); } - CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); - try { - return createDisk(serviceAccount, pool, name, diskOfferingId, provisionedSizeInGb, initialSize); - } finally { - CallContext.unregister(); - } - } - - 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; - try { - volume = volumeApiService.allocVolume(serviceAccount.getId(), pool.getDataCenterId(), diskOfferingId, null, - null, name, sizeInGb, null, null, null, null); - } catch (ResourceAllocationException e) { - throw new CloudRuntimeException(e.getMessage(), e); - } - if (volume == null) { - throw new CloudRuntimeException("Failed to create volume"); - } - volume = volumeApiService.createVolume(volume.getId(), null, null, pool.getId(), true); - if (initialSize != null) { - volumeDetailsDao.addDetail(volume.getId(), ApiConstants.VIRTUAL_SIZE, String.valueOf(initialSize), true); - } - - // Implementation for creating a Disk resource - return VolumeJoinVOToDiskConverter.toDisk(volumeJoinDao.findById(volume.getId()), this::getVolumePhysicalSize); - } - - protected List listNicsByInstance(final long instanceId, final String instanceUuid) { - List nics = nicDao.listByVmId(instanceId); - return NicVOToNicConverter.toNicList(nics, instanceUuid, this::getNetworkById); - } - - protected List listNicsByInstance(final UserVmJoinVO vo) { - return listNicsByInstance(vo.getId(), vo.getUuid()); + return createDisk(caller, pool, name, diskOfferingId, provisionedSizeInGb, initialSize); } + @ApiAccess(command = ListNicsCmd.class) public List listNicsByInstanceUuid(final String uuid) { UserVmVO vo = userVmDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); } - accountService.checkAccess(getServiceAccount().second(), null, false, vo); + accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, vo); return listNicsByInstance(vo.getId(), vo.getUuid()); } - protected boolean accountCannotAccessNetwork(NetworkVO networkVO, long accountId) { - Account account = accountService.getActiveAccountById(accountId); - try { - networkModel.checkNetworkPermissions(account, networkVO); - return false; - } catch (CloudRuntimeException e) { - logger.debug("{} cannot access {}: {}", account, networkVO, e.getMessage()); - } - return true; - } - - protected void assignVmToAccount(UserVmVO vmVO, long accountId, Pair serviceUserAccount) { - Account account = accountService.getActiveAccountById(accountId); - if (account == null) { - throw new InvalidParameterValueException("Account with ID " + accountId + " not found"); - } - CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); - try { - AssignVMCmd cmd = new AssignVMCmd(); - ComponentContext.inject(cmd); - cmd.setVirtualMachineId(vmVO.getId()); - cmd.setDomainId(account.getDomainId()); - if (Account.Type.PROJECT.equals(account.getType())) { - Project project = projectService.findByProjectAccountId(account.getId()); - if (project == null) { - throw new InvalidParameterValueException("Project for " + account + " not found"); - } - cmd.setProjectId(project.getId()); - } else { - cmd.setAccountName(account.getAccountName()); - } - cmd.setSkipNetwork(true); - userVmManager.moveVmToUser(cmd); - } catch (ResourceAllocationException | CloudRuntimeException | ResourceUnavailableException | - InsufficientCapacityException e) { - logger.error("Failed to assign {} to {}: {}", vmVO, account, e.getMessage(), e); - } finally { - CallContext.unregister(); - } - } - + @ApiAccess(command = AddNicToVMCmd.class) public Nic attachInstanceNic(final String vmUuid, final Nic request) { UserVmVO vmVo = userVmDao.findByUuid(vmUuid); if (vmVo == null) { throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found"); } - Pair serviceUserAccount = getServiceAccount(); - accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, vmVo); + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, + false, vmVo); if (request == null || request.getVnicProfile() == null || StringUtils.isEmpty(request.getVnicProfile().getId())) { throw new InvalidParameterValueException("Request nic data is empty"); } @@ -1259,48 +1356,49 @@ public class ServerAdapter extends ManagerBase { if (networkVO == null) { throw new InvalidParameterValueException("VNic profile " + request.getVnicProfile().getId() + " not found"); } - accountService.checkAccess(serviceUserAccount.second(), SecurityChecker.AccessType.OperateEntry, false, networkVO); + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, + false, networkVO); if (vmVo.getAccountId() != networkVO.getAccountId() && networkVO.getAccountId() != Account.ACCOUNT_ID_SYSTEM && VeeamControlService.InstanceRestoreAssignOwner.value() && accountCannotAccessNetwork(networkVO, vmVo.getAccountId())) { - assignVmToAccount(vmVo, networkVO.getAccountId(), serviceUserAccount); + assignVmToAccount(vmVo, networkVO.getAccountId()); } - CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); - try { - AddNicToVMCmd cmd = new AddNicToVMCmd(); - ComponentContext.inject(cmd); - cmd.setVmId(vmVo.getId()); - cmd.setNetworkId(networkVO.getId()); - if (request.getMac() != null && StringUtils.isNotBlank(request.getMac().getAddress())) { - cmd.setMacAddress(request.getMac().getAddress()); - } - userVmManager.addNicToVirtualMachine(cmd); - NicVO nic = nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(networkVO.getId(), vmVo.getId()); - if (nic == null) { - throw new CloudRuntimeException("Failed to attach NIC to VM"); - } - return NicVOToNicConverter.toNic(nic, vmUuid, this::getNetworkById); - } finally { - CallContext.unregister(); + AddNicToVMCmd cmd = new AddNicToVMCmd(); + ComponentContext.inject(cmd); + cmd.setVmId(vmVo.getId()); + cmd.setNetworkId(networkVO.getId()); + if (request.getMac() != null && StringUtils.isNotBlank(request.getMac().getAddress())) { + cmd.setMacAddress(request.getMac().getAddress()); } + userVmManager.addNicToVirtualMachine(cmd); + NicVO nic = nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(networkVO.getId(), vmVo.getId()); + if (nic == null) { + throw new CloudRuntimeException("Failed to attach NIC to VM"); + } + return NicVOToNicConverter.toNic(nic, vmUuid, this::getNetworkById); } + @ApiAccess(command = ListImageTransfersCmd.class) public List listAllImageTransfers(Long offset, Long limit) { Filter filter = new Filter(ImageTransferVO.class, "id", true, offset, limit); - List imageTransfers = imageTransferDao.listAll(filter); + Pair, List> ownerDetails = getResourceOwnerFiltersWithDomainIds(); + List imageTransfers = imageTransferDao.listByOwners(ownerDetails.first(), + ownerDetails.second(), filter); return ImageTransferVOToImageTransferConverter.toImageTransferList(imageTransfers, this::getHostById, this::getVolumeById); } + @ApiAccess(command = ListImageTransfersCmd.class) public ImageTransfer getImageTransfer(String uuid) { ImageTransferVO vo = imageTransferDao.findByUuidIncludingRemoved(uuid); if (vo == null) { throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found"); } - accountService.checkAccess(getServiceAccount().second(), null, false, vo); + accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, vo); return ImageTransferVOToImageTransferConverter.toImageTransfer(vo, this::getHostById, this::getVolumeById); } + @ApiAccess(command = CreateImageTransferCmd.class) public ImageTransfer createImageTransfer(ImageTransfer request) { if (request == null) { throw new InvalidParameterValueException("Request image transfer data is empty"); @@ -1312,8 +1410,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); + accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, + volumeVO); Direction direction = EnumUtils.fromString(Direction.class, request.getDirection()); if (direction == null) { throw new InvalidParameterValueException("Invalid or missing direction"); @@ -1327,88 +1425,47 @@ public class ServerAdapter extends ManagerBase { } backupId = backupVO.getId(); } - return createImageTransfer(backupId, volumeVO.getId(), direction, format, serviceUserAccount); + return createImageTransfer(backupId, volumeVO.getId(), direction, format); } + @ApiAccess(command = FinalizeImageTransferCmd.class) public boolean cancelImageTransfer(String uuid) { ImageTransferVO vo = imageTransferDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found"); } - accountService.checkAccess(getServiceAccount().second(), SecurityChecker.AccessType.OperateEntry, false, vo); + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, false, vo); return kvmBackupExportService.cancelImageTransfer(vo.getId()); } + @ApiAccess(command = FinalizeImageTransferCmd.class) public boolean finalizeImageTransfer(String uuid) { ImageTransferVO vo = imageTransferDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found"); } - accountService.checkAccess(getServiceAccount().second(), SecurityChecker.AccessType.OperateEntry, false, vo); + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, false, vo); return kvmBackupExportService.finalizeImageTransfer(vo.getId()); } - 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 = - kvmBackupExportService.createImageTransfer(volumeId, backupId, direction, format); - ImageTransferVO imageTransferVO = imageTransferDao.findById(imageTransfer.getId()); - return ImageTransferVOToImageTransferConverter.toImageTransfer(imageTransferVO, this::getHostById, this::getVolumeById); - } finally { - CallContext.unregister(); - } - } - - protected DataCenterJoinVO getZoneById(Long zoneId) { - if (zoneId == null) { - return null; - } - return dataCenterJoinDao.findById(zoneId); - } - - private HostJoinVO getHostById(Long hostId) { - if (hostId == null) { - return null; - } - return hostJoinDao.findById(hostId); - } - - private VolumeJoinVO getVolumeById(Long volumeId) { - if (volumeId == null) { - return null; - } - return volumeJoinDao.findById(volumeId); - } - - protected NetworkVO getNetworkById(Long networkId) { - if (networkId == null) { - return null; - } - return networkDao.findById(networkId); - } - - protected Map getDetailsByInstanceId(Long instanceId) { - return vmInstanceDetailsDao.listDetailsKeyPairs(instanceId, true); - } - + @ApiAccess(command = ListAsyncJobsCmd.class) public List listPendingJobs() { - Pair serviceUserAccount = getServiceAccount(); - List jobIds = asyncJobDao.listPendingJobIdsForAccount(serviceUserAccount.second().getId()); + List jobIds = asyncJobDao.listPendingJobIdsForAccount(CallContext.current().getCallingAccountId()); List jobJoinVOs = asyncJobJoinDao.listByIds(jobIds); return AsyncJobJoinVOToJobConverter.toJobList(jobJoinVOs); } + @ApiAccess(command = ListAsyncJobsCmd.class) public Job getJob(String uuid) { final AsyncJobJoinVO vo = asyncJobJoinDao.findByUuidIncludingRemoved(uuid); if (vo == null) { throw new InvalidParameterValueException("Job with ID " + uuid + " not found"); } - accountService.checkAccess(getServiceAccount().second(), null, false, vo); + accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, vo); return AsyncJobJoinVOToJobConverter.toJob(vo); } + @ApiAccess(command = ListVMSnapshotCmd.class) public List listSnapshotsByInstanceUuid(final String uuid) { UserVmVO vo = userVmDao.findByUuid(uuid); if (vo == null) { @@ -1418,14 +1475,14 @@ public class ServerAdapter extends ManagerBase { return VmSnapshotVOToSnapshotConverter.toSnapshotList(snapshots, vo.getUuid()); } + @ApiAccess(command = CreateVMSnapshotCmd.class) public Snapshot createInstanceSnapshot(final String vmUuid, final Snapshot request) { UserVmVO vmVo = userVmDao.findByUuid(vmUuid); if (vmVo == null) { 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()); + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, + false, vmVo); try { CreateVMSnapshotCmd cmd = new CreateVMSnapshotCmd(); ComponentContext.inject(cmd); @@ -1433,9 +1490,7 @@ public class ServerAdapter extends ManagerBase { params.put(ApiConstants.VIRTUAL_MACHINE_ID, vmVo.getUuid()); params.put(ApiConstants.VM_SNAPSHOT_DESCRIPTION, request.getDescription()); params.put(ApiConstants.VM_SNAPSHOT_MEMORY, String.valueOf(Boolean.parseBoolean(request.getPersistMemorystate()))); - ApiServerService.AsyncCmdResult result = - apiServerService.processAsyncCmd(cmd, params, ctx, serviceUserAccount.first().getId(), - serviceUserAccount.second()); + ApiServerService.AsyncCmdResult result = processAsyncCmdWithContext(cmd, params); if (result.objectId == null) { throw new CloudRuntimeException("No snapshot ID returned"); } @@ -1446,17 +1501,16 @@ public class ServerAdapter extends ManagerBase { return VmSnapshotVOToSnapshotConverter.toSnapshot(vo, vmVo.getUuid()); } catch (Exception e) { throw new CloudRuntimeException("Failed to create snapshot: " + e.getMessage(), e); - } finally { - CallContext.unregister(); } } + @ApiAccess(command = ListVMSnapshotCmd.class) public Snapshot getSnapshot(String uuid) { VMSnapshotVO vo = vmSnapshotDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("Snapshot with ID " + uuid + " not found"); } - accountService.checkAccess(getServiceAccount().second(), null, false, vo); + accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, vo); UserVmVO vm = userVmDao.findById(vo.getVmId()); return VmSnapshotVOToSnapshotConverter.toSnapshot(vo, vm.getUuid()); } @@ -1467,17 +1521,14 @@ public class ServerAdapter extends ManagerBase { if (vo == null) { 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()); + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, + false, vo); try { DeleteVMSnapshotCmd cmd = new DeleteVMSnapshotCmd(); ComponentContext.inject(cmd); Map params = new HashMap<>(); params.put(ApiConstants.VM_SNAPSHOT_ID, vo.getUuid()); - ApiServerService.AsyncCmdResult result = - apiServerService.processAsyncCmd(cmd, params, ctx, serviceUserAccount.first().getId(), - serviceUserAccount.second()); + ApiServerService.AsyncCmdResult result = processAsyncCmdWithContext(cmd, params); AsyncJobJoinVO jobVo = asyncJobJoinDao.findById(result.jobId); if (jobVo == null) { throw new CloudRuntimeException("Failed to find job for snapshot deletion"); @@ -1488,29 +1539,25 @@ public class ServerAdapter extends ManagerBase { action = AsyncJobJoinVOToJobConverter.toAction(jobVo); } catch (Exception e) { throw new CloudRuntimeException("Failed to delete snapshot: " + e.getMessage(), e); - } finally { - CallContext.unregister(); } return action; } + @ApiAccess(command = RevertToVMSnapshotCmd.class) public ResourceAction revertInstanceToSnapshot(String uuid, boolean async) { ResourceAction action = null; VMSnapshotVO vo = vmSnapshotDao.findByUuid(uuid); if (vo == null) { 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()); + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, + false, vo); try { RevertToVMSnapshotCmd cmd = new RevertToVMSnapshotCmd(); ComponentContext.inject(cmd); Map params = new HashMap<>(); params.put(ApiConstants.VM_SNAPSHOT_ID, vo.getUuid()); - ApiServerService.AsyncCmdResult result = - apiServerService.processAsyncCmd(cmd, params, ctx, serviceUserAccount.first().getId(), - serviceUserAccount.second()); + ApiServerService.AsyncCmdResult result = processAsyncCmdWithContext(cmd, params); AsyncJobJoinVO jobVo = asyncJobJoinDao.findById(result.jobId); if (jobVo == null) { throw new CloudRuntimeException("Failed to find job for snapshot revert"); @@ -1521,12 +1568,11 @@ public class ServerAdapter extends ManagerBase { action = AsyncJobJoinVOToJobConverter.toAction(jobVo); } catch (Exception e) { throw new CloudRuntimeException("Failed to revert to snapshot: " + e.getMessage(), e); - } finally { - CallContext.unregister(); } return action; } + @ApiAccess(command = ListBackupsCmd.class) public List listBackupsByInstanceUuid(final String uuid) { UserVmVO vo = userVmDao.findByUuid(uuid); if (vo == null) { @@ -1536,14 +1582,27 @@ public class ServerAdapter extends ManagerBase { return BackupVOToBackupConverter.toBackupList(backups, id -> vo, this::getHostById); } + protected void validateInstanceStorage(UserVmVO vm) { + List volumes = volumeDao.findUsableVolumesForInstance(vm.getId()); + List storageIds = volumes.stream().map(VolumeVO::getPoolId).distinct().collect(Collectors.toList()); + List pools = primaryDataStoreDao.listByIds(storageIds); + pools.stream().filter(p -> !SUPPORTED_STORAGE_TYPES.contains(p.getPoolType())) + .findAny().ifPresent(p -> { + throw new InvalidParameterValueException("VM is using storage pool " + p.getName() + + " of type " + p.getPoolType() + + " which is not supported for backup operations"); + }); + } + + @ApiAccess(command = StartBackupCmd.class) public Backup createInstanceBackup(final String vmUuid, final Backup request) { UserVmVO vmVo = userVmDao.findByUuid(vmUuid); if (vmVo == null) { 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()); + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, + false, vmVo); + validateInstanceStorage(vmVo); try { StartBackupCmd cmd = new StartBackupCmd(); ComponentContext.inject(cmd); @@ -1551,8 +1610,7 @@ public class ServerAdapter extends ManagerBase { params.put(ApiConstants.VIRTUAL_MACHINE_ID, vmVo.getUuid()); params.put(ApiConstants.NAME, request.getName()); params.put(ApiConstants.DESCRIPTION, request.getDescription()); - ApiServerService.AsyncCmdResult result = - apiServerService.processAsyncCmd(cmd, params, ctx, vmVo.getUserId(), serviceUserAccount.second()); + ApiServerService.AsyncCmdResult result = processAsyncCmdWithContext(cmd, params); if (result == null || result.objectId == null) { throw new CloudRuntimeException("Unexpected backup ID returned"); } @@ -1563,26 +1621,27 @@ public class ServerAdapter extends ManagerBase { return BackupVOToBackupConverter.toBackup(vo, id -> vmVo, this::getHostById, this::getBackupDisks); } catch (Exception e) { throw new CloudRuntimeException("Failed to create backup: " + e.getMessage(), e); - } finally { - CallContext.unregister(); } } + @ApiAccess(command = ListBackupsCmd.class) public Backup getBackup(String uuid) { BackupVO vo = backupDao.findByUuidIncludingRemoved(uuid); if (vo == null) { throw new InvalidParameterValueException("Backup with ID " + uuid + " not found"); } - accountService.checkAccess(getServiceAccount().second(), null, false, vo); + accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, vo); return BackupVOToBackupConverter.toBackup(vo, id -> userVmDao.findById(id), this::getHostById, this::getBackupDisks); } + @ApiAccess(command = ListBackupsCmd.class) public List listDisksByBackupUuid(final String uuid) { throw new InvalidParameterValueException("List Backup Disks with ID " + uuid + " not implemented"); // This won't be feasible with current structure } + @ApiAccess(command = FinalizeBackupCmd.class) public Backup finalizeBackup(final String vmUuid, final String backupUuid) { UserVmVO vm = userVmDao.findByUuid(vmUuid); if (vm == null) { @@ -1592,17 +1651,15 @@ public class ServerAdapter extends ManagerBase { if (backup == null) { 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()); + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, + false, backup); try { FinalizeBackupCmd cmd = new FinalizeBackupCmd(); ComponentContext.inject(cmd); Map params = new HashMap<>(); params.put(ApiConstants.VIRTUAL_MACHINE_ID, vm.getUuid()); params.put(ApiConstants.ID, backup.getUuid()); - ApiServerService.AsyncCmdResult result = - apiServerService.processAsyncCmd(cmd, params, ctx, vm.getUserId(), serviceUserAccount.second()); + ApiServerService.AsyncCmdResult result = processAsyncCmdWithContext(cmd, params); if (result == null) { throw new CloudRuntimeException("Failed to finalize backup"); } @@ -1610,11 +1667,10 @@ public class ServerAdapter extends ManagerBase { return BackupVOToBackupConverter.toBackup(backup, id -> vm, this::getHostById, this::getBackupDisks); } catch (Exception e) { throw new CloudRuntimeException("Failed to finalize backup: " + e.getMessage(), e); - } finally { - CallContext.unregister(); } } + @ApiAccess(command = ListBackupsCmd.class) protected List getBackupDisks(final BackupVO backup) { List volumeInfos = backup.getBackedUpVolumes(); if (CollectionUtils.isEmpty(volumeInfos)) { @@ -1623,12 +1679,13 @@ public class ServerAdapter extends ManagerBase { return VolumeJoinVOToDiskConverter.toDiskListFromVolumeInfos(volumeInfos); } + @ApiAccess(command = ListVmCheckpointsCmd.class) public List listCheckpointsByInstanceUuid(final String uuid) { UserVmVO vo = userVmDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); } - accountService.checkAccess(getServiceAccount().second(), null, false, vo); + accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, vo); Checkpoint checkpoint = UserVmVOToCheckpointConverter.toCheckpoint(vo); if (checkpoint == null) { return Collections.emptyList(); @@ -1636,18 +1693,17 @@ public class ServerAdapter extends ManagerBase { return List.of(checkpoint); } + @ApiAccess(command = DeleteVmCheckpointCmd.class) public void deleteCheckpoint(String vmUuid, String checkpointId) { UserVmVO vo = userVmDao.findByUuid(vmUuid); if (vo == null) { throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found"); } - accountService.checkAccess(getServiceAccount().second(), SecurityChecker.AccessType.OperateEntry, false, vo); + accountService.checkAccess(CallContext.current().getCallingAccount(), 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; } - Pair serviceUserAccount = getServiceAccount(); - CallContext.register(serviceUserAccount.first(), serviceUserAccount.second()); try { DeleteVmCheckpointCmd cmd = new DeleteVmCheckpointCmd(); ComponentContext.inject(cmd); @@ -1655,22 +1711,23 @@ public class ServerAdapter extends ManagerBase { kvmBackupExportService.deleteVmCheckpoint(cmd); } catch (Exception e) { throw new CloudRuntimeException("Failed to delete checkpoint: " + e.getMessage(), e); - } finally { - CallContext.unregister(); } } + @ApiAccess(command = ListTagsCmd.class) public List listAllTags(final Long offset, final Long limit) { List tags = new ArrayList<>(getDummyTags().values()); Filter filter = new Filter(ResourceTagVO.class, "id", true, offset, limit); - List vmResourceTags = resourceTagDao.listByResourceType(ResourceTag.ResourceObjectType.UserVm, - filter); + Pair, List> ownerDetails = getResourceOwnerFiltersWithDomainIds(); + List vmResourceTags = resourceTagDao.listByResourceTypeAndOwners( + ResourceTag.ResourceObjectType.UserVm, ownerDetails.first(), ownerDetails.second(), filter); if (CollectionUtils.isNotEmpty(vmResourceTags)) { tags.addAll(ResourceTagVOToTagConverter.toTags(vmResourceTags)); } return tags; } + @ApiAccess(command = ListTagsCmd.class) public Tag getTag(String uuid) { if (BaseDto.ZERO_UUID.equals(uuid)) { return ResourceTagVOToTagConverter.getRootTag(); @@ -1678,7 +1735,8 @@ 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); + accountService.checkAccess(CallContext.current().getCallingAccount(), 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/DataCentersRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DataCentersRouteHandler.java index 4ff5add7d3d..7e68375fe56 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 @@ -39,6 +39,7 @@ import org.apache.commons.collections.CollectionUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.exception.CloudRuntimeException; public class DataCentersRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/datacenters"; @@ -111,6 +112,8 @@ public class DataCentersRouteHandler extends ManagerBase implements RouteHandler io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } catch (InvalidParameterValueException e) { io.notFound(resp, e.getMessage(), outFormat); + } catch (CloudRuntimeException 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 f4cd3b6a378..0dd31675355 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 @@ -121,10 +121,14 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - 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); + try { + 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); + } catch (CloudRuntimeException e) { + io.badRequest(resp, e.getMessage(), outFormat); + } } protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp, @@ -161,15 +165,7 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler { protected void handlePutById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - String data = RouteHandler.getRequestData(req, logger); - try { - // ToDo: do what? -// serverAdapter.deleteDisk(id); - Disk response = serverAdapter.getDisk(id); - io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); - } catch (InvalidParameterValueException e) { - io.badRequest(resp, e.getMessage(), outFormat); - } + throw new InvalidParameterValueException("Put Disk with ID " + id + " not implemented"); } protected void handlePostDiskCopy(final String id, final HttpServletRequest req, final HttpServletResponse resp, 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 00b473eb6a4..ef27d6353fb 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 @@ -106,10 +106,14 @@ public class ImageTransfersRouteHandler extends ManagerBase implements RouteHand protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - 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); + try { + 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); + } catch (CloudRuntimeException e) { + io.badRequest(resp, e.getMessage(), outFormat); + } } protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp, 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 0cb03812769..50a7465414e 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 @@ -35,6 +35,7 @@ import org.apache.commons.collections.CollectionUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.exception.CloudRuntimeException; public class JobsRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/jobs"; @@ -84,9 +85,13 @@ 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.listPendingJobs(); - NamedList response = NamedList.of("job", result); - io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + try { + final List result = serverAdapter.listPendingJobs(); + NamedList response = NamedList.of("job", result); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (CloudRuntimeException e) { + io.badRequest(resp, e.getMessage(), outFormat); + } } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat 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 4014dc796fe..31e5bccca7c 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 @@ -36,6 +36,7 @@ import org.apache.commons.collections.CollectionUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.exception.CloudRuntimeException; public class NetworksRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/networks"; @@ -85,10 +86,14 @@ public class NetworksRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - 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); + try { + 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); + } catch (CloudRuntimeException e) { + io.badRequest(resp, e.getMessage(), outFormat); + } } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat 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 b571bcaa2ed..727cf72ca1a 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 @@ -36,6 +36,7 @@ import org.apache.commons.collections.CollectionUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.exception.CloudRuntimeException; public class TagsRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/tags"; @@ -86,10 +87,14 @@ public class TagsRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - 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); + try { + 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); + } catch (CloudRuntimeException e) { + io.badRequest(resp, e.getMessage(), outFormat); + } } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat 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 fdf542d6471..a3d1ca236cb 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 @@ -231,10 +231,14 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - 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); + try { + 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); + } catch (CloudRuntimeException e) { + io.badRequest(resp, e.getMessage(), outFormat); + } } protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp, @@ -308,7 +312,6 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { protected void handleStopVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { boolean async = isRequestAsync(req); - String data = RouteHandler.getRequestData(req, logger); try { VmAction vm = serverAdapter.stopInstance(id, async); io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat); 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 3e8aab2176f..31fb93ddf3b 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 @@ -36,6 +36,7 @@ import org.apache.commons.collections.CollectionUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.exception.CloudRuntimeException; public class VnicProfilesRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/vnicprofiles"; @@ -85,10 +86,14 @@ public class VnicProfilesRouteHandler extends ManagerBase implements RouteHandle protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - 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); + try { + 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); + } catch (CloudRuntimeException e) { + io.badRequest(resp, e.getMessage(), outFormat); + } } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, diff --git a/plugins/integrations/veeam-control-service/src/main/resources/META-INF/cloudstack/veeam-control-service/spring-veeam-control-service-context.xml b/plugins/integrations/veeam-control-service/src/main/resources/META-INF/cloudstack/veeam-control-service/spring-veeam-control-service-context.xml index cbe11724648..4d66d5248e0 100644 --- a/plugins/integrations/veeam-control-service/src/main/resources/META-INF/cloudstack/veeam-control-service/spring-veeam-control-service-context.xml +++ b/plugins/integrations/veeam-control-service/src/main/resources/META-INF/cloudstack/veeam-control-service/spring-veeam-control-service-context.xml @@ -18,8 +18,11 @@ --> + + + + + + 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 dc19d848193..54a98a225bc 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 @@ -22,6 +22,7 @@ import com.cloud.storage.ScopeType; import org.apache.cloudstack.api.response.StoragePoolResponse; import com.cloud.api.query.vo.StoragePoolJoinVO; +import com.cloud.storage.Storage; import com.cloud.storage.StoragePool; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; @@ -45,6 +46,6 @@ public interface StoragePoolJoinDao extends GenericDao List findStoragePoolByScopeAndRuleTags(Long datacenterId, Long podId, Long clusterId, ScopeType scopeType, List tags); - List listByZoneAndProvider(long zoneId, Filter filter); + List listByZoneAndType(long zoneId, List types, 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 35651f65794..5f4527a7c55 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 @@ -35,6 +35,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.api.ApiDBUtils; @@ -412,12 +413,16 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase listByZoneAndProvider(long zoneId, Filter filter) { + public List listByZoneAndType(long zoneId, List types, Filter filter) { SearchBuilder sb = createSearchBuilder(); sb.and("zoneId", sb.entity().getZoneId(), SearchCriteria.Op.EQ); + sb.and("types", sb.entity().getZoneId(), SearchCriteria.Op.IN); sb.done(); SearchCriteria sc = sb.create(); sc.setParameters("zoneId", zoneId); + if (CollectionUtils.isNotEmpty(types)) { + sc.setParameters("types", types.toArray()); + } 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 55d65df7ffb..0612e906666 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 @@ -52,5 +52,6 @@ public interface UserVmJoinDao extends GenericDao { List listLeaseInstancesExpiringInDays(int days); - List listByHypervisorType(Hypervisor.HypervisorType hypervisorType, Filter filter); + List listByHypervisorTypeAndOwners(Hypervisor.HypervisorType hypervisorType, List accountIds, + String domainPath, 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 d243bb7a546..94b25ccc82f 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 @@ -17,14 +17,13 @@ package com.cloud.api.query.dao; import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; import java.time.LocalDate; import java.time.ZoneId; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.Date; - import java.util.HashMap; import java.util.Hashtable; import java.util.List; @@ -34,9 +33,6 @@ import java.util.stream.Collectors; import javax.inject.Inject; -import com.cloud.gpu.dao.VgpuProfileDao; -import com.cloud.hypervisor.Hypervisor; -import com.cloud.service.dao.ServiceOfferingDao; import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -62,11 +58,14 @@ import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiResponseHelper; import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.gpu.GPU; +import com.cloud.gpu.dao.VgpuProfileDao; import com.cloud.host.ControlState; +import com.cloud.hypervisor.Hypervisor; import com.cloud.network.IpAddress; import com.cloud.network.vpc.VpcVO; import com.cloud.network.vpc.dao.VpcDao; import com.cloud.service.ServiceOfferingDetailsVO; +import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOS; import com.cloud.storage.Storage.TemplateType; @@ -96,7 +95,6 @@ import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VmStats; import com.cloud.vm.dao.NicExtraDhcpOptionDao; import com.cloud.vm.dao.NicSecondaryIpVO; - import com.cloud.vm.dao.VMInstanceDetailsDao; @Component @@ -836,12 +834,22 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation listByHypervisorType(Hypervisor.HypervisorType hypervisorType, Filter filter) { + public List listByHypervisorTypeAndOwners(Hypervisor.HypervisorType hypervisorType, + List accountIds, String domainPath, Filter filter) { SearchBuilder sb = createSearchBuilder(); sb.and("hypervisorType", sb.entity().getHypervisorType(), Op.EQ); + sb.and().op("account", sb.entity().getAccountId(), Op.IN); + sb.or("domainPath", sb.entity().getDomainPath(), Op.LIKE); + sb.cp(); sb.done(); SearchCriteria sc = sb.create(); sc.setParameters("hypervisorType", hypervisorType); + if (CollectionUtils.isNotEmpty(accountIds)) { + sc.setParameters("account", accountIds.toArray()); + } + if (StringUtils.isNotBlank(domainPath)) { + sc.setParameters("domainPath", domainPath + "%"); + } 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 c3b5859120f..7cfdfbe78e6 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 @@ -39,5 +39,6 @@ public interface VolumeJoinDao extends GenericDao { List listByInstanceId(long instanceId); - List listByHypervisor(Hypervisor.HypervisorType hypervisorType, Filter filter); + List listByHypervisorTypeAndOwners(Hypervisor.HypervisorType hypervisorType, List accountIds, + String domainPath, 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 20b6d69c591..4bcb8bff687 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 @@ -21,8 +21,6 @@ import java.util.List; import javax.inject.Inject; -import com.cloud.hypervisor.Hypervisor; -import com.cloud.offering.DiskOffering; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.api.ResponseObject.ResponseView; @@ -31,11 +29,15 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiResponseHelper; import com.cloud.api.query.vo.VolumeJoinVO; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.offering.DiskOffering; import com.cloud.offering.ServiceOffering; import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; @@ -382,14 +384,24 @@ public class VolumeJoinDaoImpl extends GenericDaoBaseWithTagInformation listByHypervisor(Hypervisor.HypervisorType hypervisorType, Filter filter) { + public List listByHypervisorTypeAndOwners(Hypervisor.HypervisorType hypervisorType, + List accountIds, String domainPath, Filter filter) { SearchBuilder sb = createSearchBuilder(); sb.and("vmType", sb.entity().getVmType(), SearchCriteria.Op.EQ); sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); + sb.and().op("account", sb.entity().getAccountId(), SearchCriteria.Op.IN); + sb.or("domainPath", sb.entity().getDomainPath(), SearchCriteria.Op.LIKE); + sb.cp(); sb.done(); SearchCriteria sc = sb.create(); sc.setParameters("vmType", VirtualMachine.Type.User); sc.setParameters("hypervisorType", hypervisorType); + if (CollectionUtils.isNotEmpty(accountIds)) { + sc.setParameters("account", accountIds.toArray()); + } + if (StringUtils.isNotBlank(domainPath)) { + sc.setParameters("domainPath", domainPath + "%"); + } return search(sc, filter); } diff --git a/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java index 2ae720fa852..ba932c775be 100644 --- a/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java @@ -23,6 +23,7 @@ import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; @@ -40,6 +41,10 @@ import org.apache.cloudstack.util.HypervisorTypeConverter; @Table(name = "volume_view") public class VolumeJoinVO extends BaseViewWithTagInformationVO implements ControlledViewEntity { + @Id + @Column(name = "id", updatable = false, nullable = false) + private long id; + @Column(name = "uuid") private String uuid; diff --git a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java index 95fdfa0fde8..a5ca5b1bf07 100644 --- a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java @@ -476,7 +476,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C return _projectAccountDao.persist(projectAccountVO); } - public ProjectAccount assignUserToProject(Project project, long userId, long accountId, Role userRole, Long projectRoleId) { + public ProjectAccount assignUserToProject(Project project, long userId, long accountId, Role userRole, Long projectRoleId) { return assignAccountToProject(project, accountId, userRole, userId, projectRoleId); } 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 4c646b5264b..adcbc5d0173 100644 --- a/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java +++ b/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java @@ -172,7 +172,8 @@ public class MockNetworkDaoImpl extends GenericDaoBase implemen } @Override - public List listByTrafficType(final TrafficType trafficType, Filter filter) { + public List listByTrafficTypeAndOwners(final TrafficType trafficType, List accountIds, + List domainIds, Filter filter) { return null; }