Merge branch '4.22'

This commit is contained in:
Daan Hoogland 2026-04-14 14:50:44 +02:00
commit 82bfa9fb3f
130 changed files with 4288 additions and 2246 deletions

View File

@ -457,3 +457,6 @@ iscsi.session.cleanup.enabled=false
# Instance conversion VIRT_V2V_TMPDIR env var
#convert.instance.env.virtv2v.tmpdir=
# Time, in seconds, to wait before retrying to rebase during the incremental snapshot process.
# incremental.snapshot.retry.rebase.wait=60

View File

@ -885,6 +885,11 @@ public class AgentProperties{
*/
public static final Property<Boolean> CREATE_FULL_CLONE = new Property<>("create.full.clone", false);
/**
* Time, in seconds, to wait before retrying to rebase during the incremental snapshot process.
* */
public static final Property<Integer> INCREMENTAL_SNAPSHOT_RETRY_REBASE_WAIT = new Property<>("incremental.snapshot.retry.rebase.wait", 60);
public static class Property <T>{
private String name;

View File

@ -26,10 +26,13 @@ public final class BucketTO {
private String secretKey;
private long accountId;
public BucketTO(Bucket bucket) {
this.name = bucket.getName();
this.accessKey = bucket.getAccessKey();
this.secretKey = bucket.getSecretKey();
this.accountId = bucket.getAccountId();
}
public BucketTO(String name) {
@ -47,4 +50,8 @@ public final class BucketTO {
public String getSecretKey() {
return this.secretKey;
}
public long getAccountId() {
return this.accountId;
}
}

View File

@ -82,7 +82,7 @@ public interface ProjectService {
Project updateProject(long id, String name, String displayText, String newOwnerName, Long userId, Role newRole) throws ResourceAllocationException;
boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType);
boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType) throws ResourceAllocationException;
boolean deleteAccountFromProject(long projectId, String accountName);
@ -100,6 +100,6 @@ public interface ProjectService {
Project findByProjectAccountIdIncludingRemoved(long projectAccountId);
boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole);
boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) throws ResourceAllocationException;
}

View File

@ -23,9 +23,10 @@ import org.apache.cloudstack.api.InternalIdentity;
public interface VMTemplateStorageResourceAssoc extends InternalIdentity {
public static enum Status {
UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED
UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, LIMIT_REACHED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED
}
List<Status> ERROR_DOWNLOAD_STATES = List.of(Status.DOWNLOAD_ERROR, Status.ABANDONED, Status.LIMIT_REACHED, Status.UNKNOWN);
List<Status> PENDING_DOWNLOAD_STATES = List.of(Status.NOT_DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS);
String getInstallPath();

View File

@ -30,6 +30,7 @@ import com.cloud.exception.ResourceAllocationException;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.template.VirtualMachineTemplate;
import org.apache.cloudstack.resourcelimit.Reserver;
public interface ResourceLimitService {
@ -191,6 +192,7 @@ public interface ResourceLimitService {
*/
public void checkResourceLimit(Account account, ResourceCount.ResourceType type, long... count) throws ResourceAllocationException;
public void checkResourceLimitWithTag(Account account, ResourceCount.ResourceType type, String tag, long... count) throws ResourceAllocationException;
public void checkResourceLimitWithTag(Account account, Long domainId, boolean considerSystemAccount, ResourceCount.ResourceType type, String tag, long... count) throws ResourceAllocationException;
/**
* Gets the count of resources for a resource type and account
@ -251,12 +253,12 @@ public interface ResourceLimitService {
List<String> getResourceLimitStorageTags(DiskOffering diskOffering);
void updateTaggedResourceLimitsAndCountsForAccounts(List<AccountResponse> responses, String tag);
void updateTaggedResourceLimitsAndCountsForDomains(List<DomainResponse> responses, String tag);
void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException;
void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List<Reserver> reservations) throws ResourceAllocationException;
List<String> getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering);
void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize,
DiskOffering currentOffering, DiskOffering newOffering) throws ResourceAllocationException;
DiskOffering currentOffering, DiskOffering newOffering, List<Reserver> reservations) throws ResourceAllocationException;
void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException;
void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List<Reserver> reservations) throws ResourceAllocationException;
void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
@ -273,25 +275,23 @@ public interface ResourceLimitService {
void incrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void decrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException;
void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Reserver> reservations) throws ResourceAllocationException;
void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template);
void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template);
void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu,
Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template) throws ResourceAllocationException;
Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template, List<Reserver> reservations) throws ResourceAllocationException;
void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering,
VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate) throws ResourceAllocationException;
VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate, List<Reserver> reservations) throws ResourceAllocationException;
void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException;
void incrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu);
void decrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu);
void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException;
void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory);
void decrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory);
void checkVmGpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu) throws ResourceAllocationException;
void incrementVmGpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu);
void decrementVmGpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu);
long recalculateDomainResourceCount(final long domainId, final ResourceType type, String tag);
}

View File

@ -127,8 +127,8 @@ public enum ApiCommandResourceType {
}
public static ApiCommandResourceType fromString(String value) {
if (StringUtils.isNotEmpty(value) && EnumUtils.isValidEnum(ApiCommandResourceType.class, value)) {
return valueOf(value);
if (StringUtils.isNotBlank(value) && EnumUtils.isValidEnumIgnoreCase(ApiCommandResourceType.class, value)) {
return EnumUtils.getEnumIgnoreCase(ApiCommandResourceType.class, value);
}
return null;
}

View File

@ -509,6 +509,7 @@ public class ApiConstants {
public static final String REPAIR = "repair";
public static final String REPETITION_ALLOWED = "repetitionallowed";
public static final String REQUIRES_HVM = "requireshvm";
public static final String RESERVED_RESOURCE_DETAILS = "reservedresourcedetails";
public static final String RESOURCES = "resources";
public static final String RESOURCE_COUNT = "resourcecount";
public static final String RESOURCE_NAME = "resourcename";

View File

@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.user.account;
import java.util.List;
import com.cloud.exception.ResourceAllocationException;
import org.apache.cloudstack.api.ApiArgValidator;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.BaseCmd;
@ -106,7 +107,7 @@ public class AddAccountToProjectCmd extends BaseAsyncCmd {
/////////////////////////////////////////////////////
@Override
public void execute() {
public void execute() throws ResourceAllocationException {
if (accountName == null && email == null) {
throw new InvalidParameterValueException("Either accountName or email is required");
}

View File

@ -17,6 +17,7 @@
package org.apache.cloudstack.api.command.user.account;
import com.cloud.exception.ResourceAllocationException;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiArgValidator;
@ -111,7 +112,7 @@ public class AddUserToProjectCmd extends BaseAsyncCmd {
/////////////////////////////////////////////////////
@Override
public void execute() {
public void execute() throws ResourceAllocationException {
validateInput();
boolean result = _projectService.addUserToProject(getProjectId(), getUsername(), getEmail(), getProjectRoleId(), getRoleType());
if (result) {

View File

@ -20,6 +20,7 @@ package org.apache.cloudstack.api.command.user.backup;
import javax.inject.Inject;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
@ -53,6 +54,7 @@ public class RestoreVolumeFromBackupAndAttachToVMCmd extends BaseAsyncCmd {
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@ACL
@Parameter(name = ApiConstants.BACKUP_ID,
type = CommandType.UUID,
entityType = BackupResponse.class,
@ -60,12 +62,14 @@ public class RestoreVolumeFromBackupAndAttachToVMCmd extends BaseAsyncCmd {
description = "ID of the Instance backup")
private Long backupId;
@ACL
@Parameter(name = ApiConstants.VOLUME_ID,
type = CommandType.STRING,
required = true,
description = "ID of the volume backed up")
private String volumeUuid;
@ACL
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
type = CommandType.UUID,
entityType = UserVmResponse.class,

View File

@ -19,6 +19,7 @@ package org.apache.cloudstack.api.command.user.job;
import java.util.Date;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiArgValidator;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListAccountResourcesCmd;
import org.apache.cloudstack.api.Parameter;
@ -40,6 +41,12 @@ public class ListAsyncJobsCmd extends BaseListAccountResourcesCmd {
@Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, description = "The id of the management server", since="4.19")
private Long managementServerId;
@Parameter(name = ApiConstants.RESOURCE_ID, validations = {ApiArgValidator.UuidString}, type = CommandType.STRING, description = "the ID of the resource associated with the job", since="4.22.1")
private String resourceId;
@Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "the type of the resource associated with the job", since="4.22.1")
private String resourceType;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -52,6 +59,14 @@ public class ListAsyncJobsCmd extends BaseListAccountResourcesCmd {
return managementServerId;
}
public String getResourceId() {
return resourceId;
}
public String getResourceType() {
return resourceType;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -16,8 +16,8 @@
// under the License.
package org.apache.cloudstack.api.command.user.job;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiArgValidator;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
@ -34,9 +34,15 @@ public class QueryAsyncJobResultCmd extends BaseCmd {
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.JOB_ID, type = CommandType.UUID, entityType = AsyncJobResponse.class, required = true, description = "The ID of the asynchronous job")
@Parameter(name = ApiConstants.JOB_ID, type = CommandType.UUID, entityType = AsyncJobResponse.class, description = "The ID of the asynchronous job")
private Long id;
@Parameter(name = ApiConstants.RESOURCE_ID, validations = {ApiArgValidator.UuidString}, type = CommandType.STRING, description = "the ID of the resource associated with the job", since="4.22.1")
private String resourceId;
@Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, description = "the type of the resource associated with the job", since="4.22.1")
private String resourceType;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -45,6 +51,14 @@ public class QueryAsyncJobResultCmd extends BaseCmd {
return id;
}
public String getResourceId() {
return resourceId;
}
public String getResourceType() {
return resourceType;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -51,6 +51,7 @@ public class CreateVMFromBackupCmd extends BaseDeployVMCmd {
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@ACL
@Parameter(name = ApiConstants.BACKUP_ID,
type = CommandType.UUID,
entityType = BackupResponse.class,

View File

@ -32,6 +32,7 @@ import org.apache.cloudstack.api.response.DiskOfferingResponse;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.ProjectResponse;
import org.apache.cloudstack.api.response.SnapshotResponse;
import org.apache.cloudstack.api.response.StoragePoolResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VolumeResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
@ -109,6 +110,13 @@ public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd implements UserC
description = "The ID of the Instance; to be used with snapshot Id, Instance to which the volume gets attached after creation")
private Long virtualMachineId;
@Parameter(name = ApiConstants.STORAGE_ID,
type = CommandType.UUID,
entityType = StoragePoolResponse.class,
description = "Storage pool ID to create the volume in. Cannot be used with the snapshotid parameter.",
authorized = {RoleType.Admin})
private Long storageId;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -153,6 +161,13 @@ public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd implements UserC
return projectId;
}
public Long getStorageId() {
if (snapshotId != null && storageId != null) {
throw new IllegalArgumentException("StorageId parameter cannot be specified with the SnapshotId parameter.");
}
return storageId;
}
public Boolean getDisplayVolume() {
return displayVolume;
}

View File

@ -85,6 +85,12 @@ public class ExtensionResponse extends BaseResponse {
@Param(description = "Removal timestamp of the extension, if applicable")
private Date removed;
@SerializedName(ApiConstants.RESERVED_RESOURCE_DETAILS)
@Param(description = "Resource detail names as comma separated string that should be reserved and not visible " +
"to end users",
since = "4.22.1")
protected String reservedResourceDetails;
public ExtensionResponse(String id, String name, String description, String type) {
this.id = id;
this.name = name;
@ -179,4 +185,8 @@ public class ExtensionResponse extends BaseResponse {
public void setRemoved(Date removed) {
this.removed = removed;
}
public void setReservedResourceDetails(String reservedResourceDetails) {
this.reservedResourceDetails = reservedResourceDetails;
}
}

View File

@ -17,8 +17,11 @@
package org.apache.cloudstack.extension;
import java.util.List;
public interface ExtensionHelper {
Long getExtensionIdForCluster(long clusterId);
Extension getExtension(long id);
Extension getExtensionForCluster(long clusterId);
List<String> getExtensionReservedResourceDetails(long extensionId);
}

View File

@ -0,0 +1,30 @@
// 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.resourcelimit;
/**
* Interface implemented by <code>CheckedReservation</code>.
* </br></br>
* This is defined in <code>cloud-api</code> to allow methods declared in modules that do not depend on <code>cloud-server</code>
* to receive <code>CheckedReservations</code> as parameters.
*/
public interface Reserver extends AutoCloseable {
void close();
}

View File

@ -16,6 +16,7 @@
// under the License.
package org.apache.cloudstack.api.command.test;
import com.cloud.exception.ResourceAllocationException;
import junit.framework.Assert;
import junit.framework.TestCase;
@ -149,6 +150,8 @@ public class AddAccountToProjectCmdTest extends TestCase {
addAccountToProjectCmd.execute();
} catch (InvalidParameterValueException exception) {
Assert.assertEquals("Either accountName or email is required", exception.getLocalizedMessage());
} catch (ResourceAllocationException exception) {
Assert.fail();
}
}

View File

@ -140,7 +140,7 @@ public class DownloadAnswer extends Answer {
}
public Long getTemplateSize() {
return templateSize;
return templateSize == 0 ? templatePhySicalSize : templateSize;
}
public void setTemplatePhySicalSize(long templatePhySicalSize) {

View File

@ -21,6 +21,7 @@ package org.apache.cloudstack.direct.download;
import com.cloud.utils.UriUtils;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.utils.security.DigestHelper;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
@ -33,6 +34,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public abstract class DirectTemplateDownloaderImpl implements DirectTemplateDownloader {
@ -128,15 +130,14 @@ public abstract class DirectTemplateDownloaderImpl implements DirectTemplateDown
*/
protected File createTemporaryDirectoryAndFile(String downloadDir) {
createFolder(downloadDir);
return new File(downloadDir + File.separator + getFileNameFromUrl());
return new File(downloadDir + File.separator + getTemporaryFileName());
}
/**
* Return filename from url
* Return filename from the temporary download file
*/
public String getFileNameFromUrl() {
String[] urlParts = url.split("/");
return urlParts[urlParts.length - 1];
public String getTemporaryFileName() {
return String.format("%s.%s", UUID.randomUUID(), FilenameUtils.getExtension(url));
}
@Override

View File

@ -97,7 +97,7 @@ public class MetalinkDirectTemplateDownloader extends DirectTemplateDownloaderIm
DirectTemplateDownloader urlDownloader = createDownloaderForMetalinks(getUrl(), getTemplateId(), getDestPoolPath(),
getChecksum(), headers, connectTimeout, soTimeout, null, temporaryDownloadPath);
try {
setDownloadedFilePath(downloadDir + File.separator + getFileNameFromUrl());
setDownloadedFilePath(downloadDir + File.separator + getTemporaryFileName());
File f = new File(getDownloadedFilePath());
if (f.exists()) {
f.delete();

View File

@ -69,7 +69,7 @@ public class NfsDirectTemplateDownloader extends DirectTemplateDownloaderImpl {
String mount = String.format(mountCommand, srcHost + ":" + srcPath, "/mnt/" + mountSrcUuid);
Script.runSimpleBashScript(mount);
String downloadDir = getDestPoolPath() + File.separator + getDirectDownloadTempPath(getTemplateId());
setDownloadedFilePath(downloadDir + File.separator + getFileNameFromUrl());
setDownloadedFilePath(downloadDir + File.separator + getTemporaryFileName());
Script.runSimpleBashScript("cp /mnt/" + mountSrcUuid + srcPath + " " + getDownloadedFilePath());
Script.runSimpleBashScript("umount /mnt/" + mountSrcUuid);
return new Pair<>(true, getDownloadedFilePath());

View File

@ -19,6 +19,9 @@
package org.apache.cloudstack.storage.command;
import com.cloud.configuration.Resource;
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
public class TemplateOrVolumePostUploadCommand {
long entityId;
@ -185,6 +188,11 @@ public class TemplateOrVolumePostUploadCommand {
this.description = description;
}
public void setDefaultMaxSecondaryStorageInBytes(long defaultMaxSecondaryStorageInBytes) {
this.defaultMaxSecondaryStorageInGB = defaultMaxSecondaryStorageInBytes != Resource.RESOURCE_UNLIMITED ?
ByteScaleUtils.bytesToGibibytes(defaultMaxSecondaryStorageInBytes) : Resource.RESOURCE_UNLIMITED;
}
public void setDefaultMaxSecondaryStorageInGB(long defaultMaxSecondaryStorageInGB) {
this.defaultMaxSecondaryStorageInGB = defaultMaxSecondaryStorageInGB;
}

View File

@ -28,6 +28,7 @@ public class UploadStatusCommand extends Command {
}
private String entityUuid;
private EntityType entityType;
private Boolean abort;
protected UploadStatusCommand() {
}
@ -37,6 +38,11 @@ public class UploadStatusCommand extends Command {
this.entityType = entityType;
}
public UploadStatusCommand(String entityUuid, EntityType entityType, Boolean abort) {
this(entityUuid, entityType);
this.abort = abort;
}
public String getEntityUuid() {
return entityUuid;
}
@ -45,6 +51,10 @@ public class UploadStatusCommand extends Command {
return entityType;
}
public Boolean getAbort() {
return abort;
}
@Override
public boolean executeInSequence() {
return false;

View File

@ -310,7 +310,7 @@ public interface NetworkOrchestrationService {
void removeDhcpServiceInSubnet(Nic nic);
boolean resourceCountNeedsUpdate(NetworkOffering ntwkOff, ACLType aclType);
boolean isResourceCountUpdateNeeded(NetworkOffering networkOffering);
void prepareAllNicsForMigration(VirtualMachineProfile vm, DeployDestination dest);

View File

@ -120,7 +120,7 @@ public interface VolumeOrchestrationService {
void destroyVolume(Volume volume);
DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template,
Account owner, Long deviceId);
Account owner, Long deviceId, boolean incrementResourceCount);
VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volume, HypervisorType rootDiskHyperType, StoragePool storagePool) throws NoTransitionException;

View File

@ -21,6 +21,7 @@ import static org.apache.cloudstack.framework.config.ConfigKey.Scope.Cluster;
import com.cloud.deploy.DeploymentPlanner;
import com.cloud.host.HostVO;
import com.cloud.host.Status;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.utils.component.Manager;
import com.cloud.vm.VMInstanceVO;
import org.apache.cloudstack.framework.config.ConfigKey;
@ -32,6 +33,8 @@ import java.util.List;
*/
public interface HighAvailabilityManager extends Manager {
List<StoragePoolType> LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT = List.of(StoragePoolType.NetworkFilesystem, StoragePoolType.SharedMountPoint);
ConfigKey<Boolean> ForceHA = new ConfigKey<>("Advanced", Boolean.class, "force.ha", "false",
"Force High-Availability to happen even if the VM says no.", true, Cluster);

View File

@ -50,7 +50,6 @@ import javax.inject.Inject;
import javax.naming.ConfigurationException;
import javax.persistence.EntityExistsException;
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
import org.apache.cloudstack.annotation.AnnotationService;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
@ -310,7 +309,6 @@ import com.cloud.vm.snapshot.VMSnapshotVO;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
import com.google.gson.Gson;
public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, VmWorkJobHandler, Listener, Configurable {
public static final String VM_WORK_JOB_HANDLER = VirtualMachineManagerImpl.class.getSimpleName();
@ -588,7 +586,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
Long deviceId = dataDiskDeviceIds.get(index++);
String volumeName = deviceId == null ? "DATA-" + persistedVm.getId() : "DATA-" + persistedVm.getId() + "-" + String.valueOf(deviceId);
volumeMgr.allocateRawVolume(Type.DATADISK, volumeName, dataDiskOfferingInfo.getDiskOffering(), dataDiskOfferingInfo.getSize(),
dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, deviceId);
dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, deviceId, true);
}
}
if (datadiskTemplateToDiskOfferingMap != null && !datadiskTemplateToDiskOfferingMap.isEmpty()) {
@ -598,7 +596,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
long diskOfferingSize = diskOffering.getDiskSize() / (1024 * 1024 * 1024);
VMTemplateVO dataDiskTemplate = _templateDao.findById(dataDiskTemplateToDiskOfferingMap.getKey());
volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + persistedVm.getId() + "-" + String.valueOf( diskNumber), diskOffering, diskOfferingSize, null, null,
persistedVm, dataDiskTemplate, owner, diskNumber);
persistedVm, dataDiskTemplate, owner, diskNumber, true);
diskNumber++;
}
}
@ -628,7 +626,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
String rootVolumeName = String.format("ROOT-%s", vm.getId());
if (template.getFormat() == ImageFormat.ISO) {
volumeMgr.allocateRawVolume(Type.ROOT, rootVolumeName, rootDiskOfferingInfo.getDiskOffering(), rootDiskOfferingInfo.getSize(),
rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vm, template, owner, null);
rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vm, template, owner, null, true);
} else if (Arrays.asList(ImageFormat.BAREMETAL, ImageFormat.EXTERNAL).contains(template.getFormat())) {
logger.debug("{} has format [{}]. Skipping ROOT volume [{}] allocation.", template, template.getFormat(), rootVolumeName);
} else {
@ -2219,7 +2217,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
protected boolean sendStop(final VirtualMachineGuru guru, final VirtualMachineProfile profile, final boolean force, final boolean checkBeforeCleanup) {
final VirtualMachine vm = profile.getVirtualMachine();
Map<String, Boolean> vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId());
StopCommand stpCmd = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), checkBeforeCleanup);
updateStopCommandForExternalHypervisorType(vm.getHypervisorType(), profile, stpCmd);
if (MapUtils.isNotEmpty(vlanToPersistenceMap)) {
@ -5278,9 +5275,20 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
private void saveCustomOfferingDetails(long vmId, ServiceOffering serviceOffering) {
Map<String, String> details = vmInstanceDetailsDao.listDetailsKeyPairs(vmId);
details.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString());
details.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString());
details.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString());
// We need to restore only the customizable parameters. If we save a parameter that is not customizable and attempt
// to restore a VM snapshot, com.cloud.vm.UserVmManagerImpl.validateCustomParameters will fail.
ServiceOffering unfilledOffering = _serviceOfferingDao.findByIdIncludingRemoved(serviceOffering.getId());
if (unfilledOffering.getCpu() == null) {
details.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString());
}
if (unfilledOffering.getSpeed() == null) {
details.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString());
}
if (unfilledOffering.getRamSize() == null) {
details.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString());
}
List<VMInstanceDetailVO> detailList = new ArrayList<>();
for (Map.Entry<String, String> entry: details.entrySet()) {
VMInstanceDetailVO detailVO = new VMInstanceDetailVO(vmId, entry.getKey(), entry.getValue(), true);

View File

@ -58,6 +58,7 @@ import org.apache.cloudstack.framework.messagebus.PublishScope;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.apache.cloudstack.network.dao.NetworkPermissionDao;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
@ -86,6 +87,7 @@ import com.cloud.api.query.dao.DomainRouterJoinDao;
import com.cloud.api.query.vo.DomainRouterJoinVO;
import com.cloud.bgp.BGPService;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.configuration.Resource;
import com.cloud.configuration.Resource.ResourceType;
import com.cloud.dc.ASNumberVO;
import com.cloud.dc.ClusterVO;
@ -214,6 +216,7 @@ import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.offerings.dao.NetworkOfferingDetailsDao;
import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
import com.cloud.resource.ResourceManager;
import com.cloud.resourcelimit.CheckedReservation;
import com.cloud.server.ManagementServer;
import com.cloud.user.Account;
import com.cloud.user.ResourceLimitService;
@ -447,6 +450,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
ClusterDao clusterDao;
@Inject
RoutedIpv4Manager routedIpv4Manager;
@Inject
private ReservationDao reservationDao;
protected StateMachine2<Network.State, Network.Event, Network> _stateMachine;
ScheduledExecutorService _executor;
@ -2752,12 +2757,6 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
return null;
}
final boolean updateResourceCount = resourceCountNeedsUpdate(ntwkOff, aclType);
//check resource limits
if (updateResourceCount) {
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.network, isDisplayNetworkEnabled);
}
// Validate network offering
if (ntwkOff.getState() != NetworkOffering.State.Enabled) {
// see NetworkOfferingVO
@ -2776,6 +2775,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
boolean ipv6 = false;
try (CheckedReservation networkReservation = new CheckedReservation(owner, domainId, Resource.ResourceType.network, null, null, 1L, reservationDao, _resourceLimitMgr)) {
if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) {
ipv6 = true;
}
@ -3115,8 +3116,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
}
}
if (updateResourceCount) {
_resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.network, isDisplayNetworkEnabled);
if (isResourceCountUpdateNeeded(ntwkOff)) {
changeAccountResourceCountOrRecalculateDomainResourceCount(owner.getAccountId(), domainId, isDisplayNetworkEnabled, true);
}
UsageEventUtils.publishNetworkCreation(network);
@ -3127,6 +3128,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
CallContext.current().setEventDetails("Network ID: " + network.getUuid());
CallContext.current().putContextParameter(Network.class, network.getUuid());
return network;
}
}
@Override
@ -3492,9 +3494,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
}
final NetworkOffering ntwkOff = _entityMgr.findById(NetworkOffering.class, networkFinal.getNetworkOfferingId());
final boolean updateResourceCount = resourceCountNeedsUpdate(ntwkOff, networkFinal.getAclType());
if (updateResourceCount) {
_resourceLimitMgr.decrementResourceCount(networkFinal.getAccountId(), ResourceType.network, networkFinal.getDisplayNetwork());
if (isResourceCountUpdateNeeded(ntwkOff)) {
changeAccountResourceCountOrRecalculateDomainResourceCount(networkFinal.getAccountId(), networkFinal.getDomainId(), networkFinal.getDisplayNetwork(), false);
}
}
return deletedVlans.second();
@ -3517,6 +3518,23 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
return success;
}
/**
* If it is a shared network with {@link ACLType#Domain}, it will belong to account {@link Account#ACCOUNT_ID_SYSTEM} and the resources will be not incremented for the
* domain. Therefore, we force the recalculation of the domain's resource count in this case. Otherwise, it will change the count for the account owner.
* @param incrementAccountResourceCount If true, the account resource count will be incremented by 1; otherwise, it will decremented by 1.
*/
private void changeAccountResourceCountOrRecalculateDomainResourceCount(Long accountId, Long domainId, boolean displayNetwork, boolean incrementAccountResourceCount) {
if (Account.ACCOUNT_ID_SYSTEM == accountId && ObjectUtils.isNotEmpty(domainId)) {
_resourceLimitMgr.recalculateDomainResourceCount(domainId, ResourceType.network, null);
} else {
if (incrementAccountResourceCount) {
_resourceLimitMgr.incrementResourceCount(accountId, ResourceType.network, displayNetwork);
} else {
_resourceLimitMgr.decrementResourceCount(accountId, ResourceType.network, displayNetwork);
}
}
}
private void publishDeletedVlanRanges(List<VlanVO> deletedVlanRangeToPublish) {
if (CollectionUtils.isNotEmpty(deletedVlanRangeToPublish)) {
for (VlanVO vlan : deletedVlanRangeToPublish) {
@ -3526,10 +3544,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
}
@Override
public boolean resourceCountNeedsUpdate(final NetworkOffering ntwkOff, final ACLType aclType) {
//Update resource count only for Isolated account specific non-system networks
final boolean updateResourceCount = ntwkOff.getGuestType() == GuestType.Isolated && !ntwkOff.isSystemOnly() && aclType == ACLType.Account;
return updateResourceCount;
public boolean isResourceCountUpdateNeeded(NetworkOffering networkOffering) {
return !networkOffering.isSystemOnly();
}
protected Pair<Boolean, List<VlanVO>> deleteVlansInNetwork(final NetworkVO network, final long userId, final Account callerAccount) {

View File

@ -40,6 +40,7 @@ import javax.naming.ConfigurationException;
import com.cloud.deploy.DeploymentClusterPlanner;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.resourcelimit.ReservationHelper;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateDao;
@ -82,6 +83,7 @@ import org.apache.cloudstack.framework.jobs.AsyncJobManager;
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.apache.cloudstack.resourcelimit.Reserver;
import org.apache.cloudstack.secret.PassphraseVO;
import org.apache.cloudstack.secret.dao.PassphraseDao;
import org.apache.cloudstack.snapshot.SnapshotHelper;
@ -862,7 +864,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", create = true)
@Override
public DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template, Account owner,
Long deviceId) {
Long deviceId, boolean incrementResourceCount) {
if (size == null) {
size = offering.getDiskSize();
} else {
@ -901,7 +903,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
saveVolumeDetails(offering.getId(), vol.getId());
// Save usage event and update resource count for user vm volumes
if (vm.getType() == VirtualMachine.Type.User) {
if (vm.getType() == VirtualMachine.Type.User && incrementResourceCount) {
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(), vol.getDataCenterId(), vol.getId(), vol.getName(), offering.getId(), null, size,
Volume.class.getName(), vol.getUuid(), vol.getInstanceId(), vol.isDisplayVolume());
_resourceLimitMgr.incrementVolumeResourceCount(vm.getAccountId(), vol.isDisplayVolume(), vol.getSize(), offering);
@ -1938,14 +1940,20 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
template == null ? null : template.getSize(),
vol.getPassphraseId() != null);
if (newSize != vol.getSize()) {
DiskOfferingVO diskOffering = diskOfferingDao.findByIdIncludingRemoved(vol.getDiskOfferingId());
if (newSize == vol.getSize()) {
return;
}
DiskOfferingVO diskOffering = diskOfferingDao.findByIdIncludingRemoved(vol.getDiskOfferingId());
List<Reserver> reservations = new ArrayList<>();
try {
VMInstanceVO vm = vol.getInstanceId() != null ? vmInstanceDao.findById(vol.getInstanceId()) : null;
if (vm == null || vm.getType() == VirtualMachine.Type.User) {
// Update resource count for user vm volumes when volume is attached
if (newSize > vol.getSize()) {
_resourceLimitMgr.checkPrimaryStorageResourceLimit(_accountMgr.getActiveAccountById(vol.getAccountId()),
vol.isDisplay(), newSize - vol.getSize(), diskOffering);
vol.isDisplay(), newSize - vol.getSize(), diskOffering, reservations);
_resourceLimitMgr.incrementVolumePrimaryStorageResourceCount(vol.getAccountId(), vol.isDisplay(),
newSize - vol.getSize(), diskOffering);
} else {
@ -1953,9 +1961,11 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
vol.getSize() - newSize, diskOffering);
}
}
vol.setSize(newSize);
_volsDao.persist(vol);
} finally {
ReservationHelper.closeAll(reservations);
}
vol.setSize(newSize);
_volsDao.persist(vol);
}
@Override

View File

@ -27,6 +27,6 @@ public interface AccountVlanMapDao extends GenericDao<AccountVlanMapVO, Long> {
public List<AccountVlanMapVO> listAccountVlanMapsByVlan(long vlanDbId);
public AccountVlanMapVO findAccountVlanMap(long accountId, long vlanDbId);
public AccountVlanMapVO findAccountVlanMap(Long accountId, long vlanDbId);
}

View File

@ -48,9 +48,9 @@ public class AccountVlanMapDaoImpl extends GenericDaoBase<AccountVlanMapVO, Long
}
@Override
public AccountVlanMapVO findAccountVlanMap(long accountId, long vlanDbId) {
public AccountVlanMapVO findAccountVlanMap(Long accountId, long vlanDbId) {
SearchCriteria<AccountVlanMapVO> sc = AccountVlanSearch.create();
sc.setParameters("accountId", accountId);
sc.setParametersIfNotNull("accountId", accountId);
sc.setParameters("vlanDbId", vlanDbId);
return findOneIncludingRemovedBy(sc);
}

View File

@ -24,5 +24,5 @@ import com.cloud.utils.db.GenericDao;
public interface DomainVlanMapDao extends GenericDao<DomainVlanMapVO, Long> {
public List<DomainVlanMapVO> listDomainVlanMapsByDomain(long domainId);
public List<DomainVlanMapVO> listDomainVlanMapsByVlan(long vlanDbId);
public DomainVlanMapVO findDomainVlanMap(long domainId, long vlanDbId);
public DomainVlanMapVO findDomainVlanMap(Long domainId, long vlanDbId);
}

View File

@ -46,9 +46,9 @@ public class DomainVlanMapDaoImpl extends GenericDaoBase<DomainVlanMapVO, Long>
}
@Override
public DomainVlanMapVO findDomainVlanMap(long domainId, long vlanDbId) {
public DomainVlanMapVO findDomainVlanMap(Long domainId, long vlanDbId) {
SearchCriteria<DomainVlanMapVO> sc = DomainVlanSearch.create();
sc.setParameters("domainId", domainId);
sc.setParametersIfNotNull("domainId", domainId);
sc.setParameters("vlanDbId", vlanDbId);
return findOneIncludingRemovedBy(sc);
}

View File

@ -21,6 +21,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.utils.Pair;
@ -192,4 +193,8 @@ public interface VMInstanceDao extends GenericDao<VMInstanceVO, Long>, StateDao<
int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List<Long> domainIds);
List<VMInstanceVO> listByIdsIncludingRemoved(List<Long> ids);
List<VMInstanceVO> listDeleteProtectedVmsByAccountId(long accountId);
List<VMInstanceVO> listDeleteProtectedVmsByDomainIds(Set<Long> domainIds);
}

View File

@ -25,11 +25,13 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;
@ -106,6 +108,8 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
protected SearchBuilder<VMInstanceVO> IdsPowerStateSelectSearch;
GenericSearchBuilder<VMInstanceVO, Integer> CountByOfferingId;
GenericSearchBuilder<VMInstanceVO, Integer> CountUserVmNotInDomain;
SearchBuilder<VMInstanceVO> DeleteProtectedVmSearchByAccount;
SearchBuilder<VMInstanceVO> DeleteProtectedVmSearchByDomainIds;
@Inject
ResourceTagDao tagsDao;
@ -368,6 +372,19 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
CountUserVmNotInDomain.and("domainIdsNotIn", CountUserVmNotInDomain.entity().getDomainId(), Op.NIN);
CountUserVmNotInDomain.done();
DeleteProtectedVmSearchByAccount = createSearchBuilder();
DeleteProtectedVmSearchByAccount.selectFields(DeleteProtectedVmSearchByAccount.entity().getUuid());
DeleteProtectedVmSearchByAccount.and(ApiConstants.ACCOUNT_ID, DeleteProtectedVmSearchByAccount.entity().getAccountId(), Op.EQ);
DeleteProtectedVmSearchByAccount.and(ApiConstants.DELETE_PROTECTION, DeleteProtectedVmSearchByAccount.entity().isDeleteProtection(), Op.EQ);
DeleteProtectedVmSearchByAccount.and(ApiConstants.REMOVED, DeleteProtectedVmSearchByAccount.entity().getRemoved(), Op.NULL);
DeleteProtectedVmSearchByAccount.done();
DeleteProtectedVmSearchByDomainIds = createSearchBuilder();
DeleteProtectedVmSearchByDomainIds.selectFields(DeleteProtectedVmSearchByDomainIds.entity().getUuid());
DeleteProtectedVmSearchByDomainIds.and(ApiConstants.DOMAIN_IDS, DeleteProtectedVmSearchByDomainIds.entity().getDomainId(), Op.IN);
DeleteProtectedVmSearchByDomainIds.and(ApiConstants.DELETE_PROTECTION, DeleteProtectedVmSearchByDomainIds.entity().isDeleteProtection(), Op.EQ);
DeleteProtectedVmSearchByDomainIds.and(ApiConstants.REMOVED, DeleteProtectedVmSearchByDomainIds.entity().getRemoved(), Op.NULL);
DeleteProtectedVmSearchByDomainIds.done();
}
@Override
@ -1296,4 +1313,22 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
sc.setParameters("ids", ids.toArray());
return listIncludingRemovedBy(sc);
}
@Override
public List<VMInstanceVO> listDeleteProtectedVmsByAccountId(long accountId) {
SearchCriteria<VMInstanceVO> sc = DeleteProtectedVmSearchByAccount.create();
sc.setParameters(ApiConstants.ACCOUNT_ID, accountId);
sc.setParameters(ApiConstants.DELETE_PROTECTION, true);
Filter filter = new Filter(VMInstanceVO.class, null, false, 0L, 10L);
return listBy(sc, filter);
}
@Override
public List<VMInstanceVO> listDeleteProtectedVmsByDomainIds(Set<Long> domainIds) {
SearchCriteria<VMInstanceVO> sc = DeleteProtectedVmSearchByDomainIds.create();
sc.setParameters(ApiConstants.DOMAIN_IDS, domainIds.toArray());
sc.setParameters(ApiConstants.DELETE_PROTECTION, true);
Filter filter = new Filter(VMInstanceVO.class, null, false, 0L, 10L);
return listBy(sc, filter);
}
}

View File

@ -21,20 +21,14 @@ import java.util.Date;
import java.util.List;
import com.cloud.utils.DateUtil;
import org.apache.cloudstack.api.response.BackupScheduleResponse;
import org.apache.cloudstack.backup.BackupSchedule;
import org.apache.cloudstack.backup.BackupScheduleVO;
import com.cloud.utils.db.GenericDao;
public interface BackupScheduleDao extends GenericDao<BackupScheduleVO, Long> {
BackupScheduleVO findByVM(Long vmId);
List<BackupScheduleVO> listByVM(Long vmId);
BackupScheduleVO findByVMAndIntervalType(Long vmId, DateUtil.IntervalType intervalType);
List<BackupScheduleVO> getSchedulesToExecute(Date currentTimestamp);
BackupScheduleResponse newBackupScheduleResponse(BackupSchedule schedule);
}

View File

@ -17,28 +17,23 @@
package org.apache.cloudstack.backup.dao;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import com.cloud.utils.DateUtil;
import org.apache.cloudstack.api.response.BackupScheduleResponse;
import org.apache.cloudstack.backup.BackupSchedule;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.TransactionLegacy;
import org.apache.cloudstack.backup.BackupScheduleVO;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.dao.VMInstanceDao;
public class BackupScheduleDaoImpl extends GenericDaoBase<BackupScheduleVO, Long> implements BackupScheduleDao {
@Inject
VMInstanceDao vmInstanceDao;
private SearchBuilder<BackupScheduleVO> backupScheduleSearch;
private SearchBuilder<BackupScheduleVO> executableSchedulesSearch;
@ -59,13 +54,6 @@ public class BackupScheduleDaoImpl extends GenericDaoBase<BackupScheduleVO, Long
executableSchedulesSearch.done();
}
@Override
public BackupScheduleVO findByVM(Long vmId) {
SearchCriteria<BackupScheduleVO> sc = backupScheduleSearch.create();
sc.setParameters("vm_id", vmId);
return findOneBy(sc);
}
@Override
public List<BackupScheduleVO> listByVM(Long vmId) {
SearchCriteria<BackupScheduleVO> sc = backupScheduleSearch.create();
@ -88,21 +76,19 @@ public class BackupScheduleDaoImpl extends GenericDaoBase<BackupScheduleVO, Long
return listBy(sc);
}
@DB
@Override
public BackupScheduleResponse newBackupScheduleResponse(BackupSchedule schedule) {
VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(schedule.getVmId());
BackupScheduleResponse response = new BackupScheduleResponse();
response.setId(schedule.getUuid());
response.setVmId(vm.getUuid());
response.setVmName(vm.getHostName());
response.setIntervalType(schedule.getScheduleType());
response.setSchedule(schedule.getSchedule());
response.setTimezone(schedule.getTimezone());
response.setMaxBackups(schedule.getMaxBackups());
if (schedule.getQuiesceVM() != null) {
response.setQuiesceVM(schedule.getQuiesceVM());
public boolean remove(Long id) {
String sql = "UPDATE backups SET backup_schedule_id = NULL WHERE backup_schedule_id = ?";
TransactionLegacy transaction = TransactionLegacy.currentTxn();
try {
PreparedStatement preparedStatement = transaction.prepareAutoCloseStatement(sql);
preparedStatement.setLong(1, id);
preparedStatement.executeUpdate();
return super.remove(id);
} catch (SQLException e) {
logger.warn("Unable to clean up backup schedules references from the backups table.", e);
return false;
}
response.setObjectName("backupschedule");
return response;
}
}

View File

@ -34,7 +34,22 @@ UPDATE `cloud`.`alert` SET type = 34 WHERE name = 'ALERT.VR.PRIVATE.IFACE.MTU';
-- Update configuration 'kvm.ssh.to.agent' description and is_dynamic fields
UPDATE `cloud`.`configuration` SET description = 'True if the management server will restart the agent service via SSH into the KVM hosts after or during maintenance operations', is_dynamic = 1 WHERE name = 'kvm.ssh.to.agent';
-- Sanitize legacy network-level addressing fields for Public networks
UPDATE `cloud`.`networks`
SET `broadcast_uri` = NULL,
`gateway` = NULL,
`cidr` = NULL,
`ip6_gateway` = NULL,
`ip6_cidr` = NULL
WHERE `traffic_type` = 'Public';
UPDATE `cloud`.`vm_template` SET guest_os_id = 99 WHERE name = 'kvm-default-vm-import-dummy-template';
-- Update existing vm_template records with NULL type to "USER"
UPDATE `cloud`.`vm_template` SET `type` = 'USER' WHERE `type` IS NULL;
-- remove unused config item
DELETE FROM `cloud`.`configuration` WHERE name = 'consoleproxy.cmd.port';
-- Drops the unused "backup_interval_type" column of the "cloud.backups" table
ALTER TABLE `cloud`.`backups` DROP COLUMN `backup_interval_type`;

View File

@ -79,6 +79,7 @@ SELECT
`vm_template`.`format` AS `template_format`,
`vm_template`.`display_text` AS `template_display_text`,
`vm_template`.`enable_password` AS `password_enabled`,
`vm_template`.`extension_id` AS `template_extension_id`,
`iso`.`id` AS `iso_id`,
`iso`.`uuid` AS `iso_uuid`,
`iso`.`name` AS `iso_name`,

View File

@ -111,7 +111,7 @@ public class StorageVMSnapshotStrategy extends DefaultVMSnapshotStrategy {
FreezeThawVMAnswer freezeAnswer = null;
FreezeThawVMCommand thawCmd = null;
FreezeThawVMAnswer thawAnswer = null;
List<SnapshotInfo> forRollback = new ArrayList<>();
List<SnapshotInfo> snapshotsForRollback = new ArrayList<>();
long startFreeze = 0;
try {
vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshotVO, VMSnapshot.Event.CreateRequested);
@ -165,7 +165,7 @@ public class StorageVMSnapshotStrategy extends DefaultVMSnapshotStrategy {
logger.info("The virtual machine is frozen");
for (VolumeInfo vol : vinfos) {
long startSnapshtot = System.nanoTime();
SnapshotInfo snapInfo = createDiskSnapshot(vmSnapshot, forRollback, vol);
SnapshotInfo snapInfo = createDiskSnapshot(vmSnapshot, snapshotsForRollback, vol);
if (snapInfo == null) {
thawAnswer = (FreezeThawVMAnswer) agentMgr.send(hostId, thawCmd);
@ -222,7 +222,7 @@ public class StorageVMSnapshotStrategy extends DefaultVMSnapshotStrategy {
}
}
if (!result) {
for (SnapshotInfo snapshotInfo : forRollback) {
for (SnapshotInfo snapshotInfo : snapshotsForRollback) {
rollbackDiskSnapshot(snapshotInfo);
}
try {
@ -388,10 +388,16 @@ public class StorageVMSnapshotStrategy extends DefaultVMSnapshotStrategy {
//Rollback if one of disks snapshot fails
protected void rollbackDiskSnapshot(SnapshotInfo snapshotInfo) {
if (snapshotInfo == null) {
return;
}
Long snapshotID = snapshotInfo.getId();
SnapshotVO snapshot = snapshotDao.findById(snapshotID);
if (snapshot == null) {
return;
}
deleteSnapshotByStrategy(snapshot);
logger.debug("Rollback is executed: deleting snapshot with id:" + snapshotID);
logger.debug("Rollback is executed: deleting snapshot with id: {}", snapshotID);
}
protected void deleteSnapshotByStrategy(SnapshotVO snapshot) {
@ -434,7 +440,7 @@ public class StorageVMSnapshotStrategy extends DefaultVMSnapshotStrategy {
}
}
protected SnapshotInfo createDiskSnapshot(VMSnapshot vmSnapshot, List<SnapshotInfo> forRollback, VolumeInfo vol) {
protected SnapshotInfo createDiskSnapshot(VMSnapshot vmSnapshot, List<SnapshotInfo> snapshotsForRollback, VolumeInfo vol) {
String snapshotName = vmSnapshot.getId() + "_" + vol.getUuid();
SnapshotVO snapshot = new SnapshotVO(vol.getDataCenterId(), vol.getAccountId(), vol.getDomainId(), vol.getId(), vol.getDiskOfferingId(),
snapshotName, (short) Snapshot.Type.GROUP.ordinal(), Snapshot.Type.GROUP.name(), vol.getSize(), vol.getMinIops(), vol.getMaxIops(), Hypervisor.HypervisorType.KVM, null);
@ -448,6 +454,7 @@ public class StorageVMSnapshotStrategy extends DefaultVMSnapshotStrategy {
vol.addPayload(setPayload(vol, snapshot, quiescevm));
SnapshotInfo snapshotInfo = snapshotDataFactory.getSnapshot(snapshot.getId(), vol.getDataStore());
snapshotInfo.addPayload(vol.getpayload());
snapshotsForRollback.add(snapshotInfo);
SnapshotStrategy snapshotStrategy = storageStrategyFactory.getSnapshotStrategy(snapshotInfo, SnapshotOperation.TAKE);
if (snapshotStrategy == null) {
throw new CloudRuntimeException("Could not find strategy for snapshot uuid:" + snapshotInfo.getUuid());
@ -455,8 +462,6 @@ public class StorageVMSnapshotStrategy extends DefaultVMSnapshotStrategy {
snapshotInfo = snapshotStrategy.takeSnapshot(snapshotInfo);
if (snapshotInfo == null) {
throw new CloudRuntimeException("Failed to create snapshot");
} else {
forRollback.add(snapshotInfo);
}
vmSnapshotDetailsDao.persist(new VMSnapshotDetailsVO(vmSnapshot.getId(), STORAGE_SNAPSHOT, String.valueOf(snapshot.getId()), true));
snapshotInfo.markBackedUp();

View File

@ -155,7 +155,7 @@ public class VMSnapshotStrategyKVMTest extends TestCase{
@Test
public void testCreateDiskSnapshotBasedOnStrategy() throws Exception {
VMSnapshotVO vmSnapshot = Mockito.mock(VMSnapshotVO.class);
List<SnapshotInfo> forRollback = new ArrayList<>();
List<SnapshotInfo> snapshotsForRollback = new ArrayList<>();
VolumeInfo vol = Mockito.mock(VolumeInfo.class);
SnapshotInfo snapshotInfo = Mockito.mock(SnapshotInfo.class);
SnapshotStrategy strategy = Mockito.mock(SnapshotStrategy.class);
@ -179,7 +179,7 @@ public class VMSnapshotStrategyKVMTest extends TestCase{
VMSnapshotDetailsVO vmDetails = new VMSnapshotDetailsVO(vmSnapshot.getId(), volUuid, String.valueOf(snapshot.getId()), false);
when(vmSnapshotDetailsDao.persist(any())).thenReturn(vmDetails);
info = vmStrategy.createDiskSnapshot(vmSnapshot, forRollback, vol);
info = vmStrategy.createDiskSnapshot(vmSnapshot, snapshotsForRollback, vol);
assertNotNull(info);
}

View File

@ -145,10 +145,10 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
storageType = "shared";
}
logger.debug(String.format(
"Filtering storage pools by capacity type [%s] as the first storage pool of the list, with name [%s] and ID [%s], is a [%s] storage.",
logger.info(
"Filtering storage pools by capacity type [{}] as the first storage pool of the list, with name [{}] and ID [{}], is a [{}] storage.",
capacityType, storagePool.getName(), storagePool.getUuid(), storageType
));
);
Pair<List<Long>, Map<Long, Double>> result = capacityDao.orderHostsByFreeCapacity(zoneId, clusterId, capacityType);
List<Long> poolIdsByCapacity = result.first();
@ -185,7 +185,7 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
Long clusterId = plan.getClusterId();
List<Long> poolIdsByVolCount = volumeDao.listPoolIdsByVolumeCount(dcId, podId, clusterId, account.getAccountId());
logger.debug(String.format("List of pools in ascending order of number of volumes for account [%s] is [%s].", account, poolIdsByVolCount));
logger.debug("List of pools in ascending order of number of volumes for account [{}] is [{}].", account, poolIdsByVolCount);
// now filter the given list of Pools by this ordered list
Map<Long, StoragePool> poolMap = new HashMap<>();
@ -206,16 +206,11 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
@Override
public List<StoragePool> reorderPools(List<StoragePool> pools, VirtualMachineProfile vmProfile, DeploymentPlan plan, DiskProfile dskCh) {
if (logger.isTraceEnabled()) {
logger.trace("reordering pools");
}
if (pools == null) {
logger.trace("There are no pools to reorder; returning null.");
logger.info("There are no pools to reorder.");
return null;
}
if (logger.isTraceEnabled()) {
logger.trace(String.format("reordering %d pools", pools.size()));
}
logger.info("Reordering [{}] pools", pools.size());
Account account = null;
if (vmProfile.getVirtualMachine() != null) {
account = vmProfile.getOwner();
@ -224,9 +219,7 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
pools = reorderStoragePoolsBasedOnAlgorithm(pools, plan, account);
if (vmProfile.getVirtualMachine() == null) {
if (logger.isTraceEnabled()) {
logger.trace("The VM is null, skipping pools reordering by disk provisioning type.");
}
logger.info("The VM is null, skipping pool reordering by disk provisioning type.");
return pools;
}
@ -240,14 +233,10 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
List<StoragePool> reorderStoragePoolsBasedOnAlgorithm(List<StoragePool> pools, DeploymentPlan plan, Account account) {
String volumeAllocationAlgorithm = VolumeOrchestrationService.VolumeAllocationAlgorithm.value();
logger.debug("Using volume allocation algorithm {} to reorder pools.", volumeAllocationAlgorithm);
logger.info("Using volume allocation algorithm {} to reorder pools.", volumeAllocationAlgorithm);
if (volumeAllocationAlgorithm.equals("random") || (account == null)) {
reorderRandomPools(pools);
} else if (StringUtils.equalsAny(volumeAllocationAlgorithm, "userdispersing", "firstfitleastconsumed")) {
if (logger.isTraceEnabled()) {
logger.trace("Using reordering algorithm {}", volumeAllocationAlgorithm);
}
if (volumeAllocationAlgorithm.equals("userdispersing")) {
pools = reorderPoolsByNumberOfVolumes(plan, pools, account);
} else {
@ -259,16 +248,15 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
void reorderRandomPools(List<StoragePool> pools) {
StorageUtil.traceLogStoragePools(pools, logger, "pools to choose from: ");
if (logger.isTraceEnabled()) {
logger.trace("Shuffle this so that we don't check the pools in the same order. Algorithm == 'random' (or no account?)");
}
StorageUtil.traceLogStoragePools(pools, logger, "pools to shuffle: ");
logger.trace("Shuffle this so that we don't check the pools in the same order. Algorithm == 'random' (or no account?)");
logger.debug("Pools to shuffle: [{}]", pools);
Collections.shuffle(pools, secureRandom);
StorageUtil.traceLogStoragePools(pools, logger, "shuffled list of pools to choose from: ");
logger.debug("Shuffled list of pools to choose from: [{}]", pools);
}
private List<StoragePool> reorderPoolsByDiskProvisioningType(List<StoragePool> pools, DiskProfile diskProfile) {
if (diskProfile != null && diskProfile.getProvisioningType() != null && !diskProfile.getProvisioningType().equals(Storage.ProvisioningType.THIN)) {
logger.info("Reordering [{}] pools by disk provisioning type [{}].", pools.size(), diskProfile.getProvisioningType());
List<StoragePool> reorderedPools = new ArrayList<>();
int preferredIndex = 0;
for (StoragePool pool : pools) {
@ -282,22 +270,28 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
reorderedPools.add(preferredIndex++, pool);
}
}
logger.debug("Reordered list of pools by disk provisioning type [{}]: [{}]", diskProfile.getProvisioningType(), reorderedPools);
return reorderedPools;
} else {
if (diskProfile == null) {
logger.info("Reordering pools by disk provisioning type wasn't necessary, since no disk profile was found.");
} else {
logger.debug("Reordering pools by disk provisioning type wasn't necessary, since the provisioning type is [{}].", diskProfile.getProvisioningType());
}
return pools;
}
}
protected boolean filter(ExcludeList avoid, StoragePool pool, DiskProfile dskCh, DeploymentPlan plan) {
logger.debug(String.format("Checking if storage pool [%s] is suitable to disk [%s].", pool, dskCh));
logger.debug("Checking if storage pool [{}] is suitable to disk [{}].", pool, dskCh);
if (avoid.shouldAvoid(pool)) {
logger.debug(String.format("StoragePool [%s] is in avoid set, skipping this pool to allocation of disk [%s].", pool, dskCh));
logger.debug("StoragePool [{}] is in avoid set, skipping this pool to allocation of disk [{}].", pool, dskCh);
return false;
}
if (dskCh.requiresEncryption() && !pool.getPoolType().supportsEncryption()) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Storage pool type '%s' doesn't support encryption required for volume, skipping this pool", pool.getPoolType()));
logger.debug("Storage pool type '[{}]' doesn't support encryption required for volume, skipping this pool", pool.getPoolType());
}
return false;
}
@ -319,8 +313,8 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
}
if (!checkDiskProvisioningSupport(dskCh, pool)) {
logger.debug(String.format("Storage pool [%s] does not have support to disk provisioning of disk [%s].", pool, ReflectionToStringBuilderUtils.reflectOnlySelectedFields(dskCh,
"type", "name", "diskOfferingId", "templateId", "volumeId", "provisioningType", "hyperType")));
logger.debug("Storage pool [{}] does not have support to disk provisioning of disk [{}].", pool, ReflectionToStringBuilderUtils.reflectOnlySelectedFields(dskCh,
"type", "name", "diskOfferingId", "templateId", "volumeId", "provisioningType", "hyperType"));
return false;
}
@ -332,7 +326,7 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
HostVO plannedHost = hostDao.findById(plan.getHostId());
if (!storageMgr.checkIfHostAndStoragePoolHasCommonStorageAccessGroups(plannedHost, pool)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("StoragePool %s and host %s does not have matching storage access groups", pool, plannedHost));
logger.debug("StoragePool [{}] and host [{}] does not have matching storage access groups", pool, plannedHost);
}
return false;
}
@ -343,13 +337,13 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
if (!isTempVolume) {
volume = volumeDao.findById(dskCh.getVolumeId());
if (!storageMgr.storagePoolCompatibleWithVolumePool(pool, volume)) {
logger.debug(String.format("Pool [%s] is not compatible with volume [%s], skipping it.", pool, volume));
logger.debug("Pool [{}] is not compatible with volume [{}], skipping it.", pool, volume);
return false;
}
}
if (pool.isManaged() && !storageUtil.managedStoragePoolCanScale(pool, plan.getClusterId(), plan.getHostId())) {
logger.debug(String.format("Cannot allocate pool [%s] to volume [%s] because the max number of managed clustered filesystems has been exceeded.", pool, volume));
logger.debug("Cannot allocate pool [{}] to volume [{}] because the max number of managed clustered filesystems has been exceeded.", pool, volume);
return false;
}
@ -358,13 +352,13 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
requestVolumeDiskProfilePairs.add(new Pair<>(volume, dskCh));
if (dskCh.getHypervisorType() == HypervisorType.VMware) {
if (pool.getPoolType() == Storage.StoragePoolType.DatastoreCluster && storageMgr.isStoragePoolDatastoreClusterParent(pool)) {
logger.debug(String.format("Skipping allocation of pool [%s] to volume [%s] because this pool is a parent datastore cluster.", pool, volume));
logger.debug("Skipping allocation of pool [{}] to volume [{}] because this pool is a parent datastore cluster.", pool, volume);
return false;
}
if (pool.getParent() != 0L) {
StoragePoolVO datastoreCluster = storagePoolDao.findById(pool.getParent());
if (datastoreCluster == null || (datastoreCluster != null && datastoreCluster.getStatus() != StoragePoolStatus.Up)) {
logger.debug(String.format("Skipping allocation of pool [%s] to volume [%s] because this pool is not in [%s] state.", datastoreCluster, volume, StoragePoolStatus.Up));
logger.debug("Skipping allocation of pool [{}] to volume [{}] because this pool is not in [{}] state.", datastoreCluster, volume, StoragePoolStatus.Up);
return false;
}
}
@ -374,11 +368,11 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
storageMgr.isStoragePoolCompliantWithStoragePolicy(dskCh.getDiskOfferingId(), pool) :
storageMgr.isStoragePoolCompliantWithStoragePolicy(requestVolumeDiskProfilePairs, pool);
if (!isStoragePoolStoragePolicyCompliance) {
logger.debug(String.format("Skipping allocation of pool [%s] to volume [%s] because this pool is not compliant with the storage policy required by the volume.", pool, volume));
logger.debug("Skipping allocation of pool [{}] to volume [{}] because this pool is not compliant with the storage policy required by the volume.", pool, volume);
return false;
}
} catch (StorageUnavailableException e) {
logger.warn(String.format("Could not verify storage policy compliance against storage pool %s due to exception %s", pool.getUuid(), e.getMessage()));
logger.warn("Could not verify storage policy compliance against storage pool [{}] due to exception [{}]", pool.getUuid(), e.getMessage());
return false;
}
}
@ -427,19 +421,19 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
protected void logDisabledStoragePools(long dcId, Long podId, Long clusterId, ScopeType scope) {
List<StoragePoolVO> disabledPools = storagePoolDao.findDisabledPoolsByScope(dcId, podId, clusterId, scope);
if (disabledPools != null && !disabledPools.isEmpty()) {
logger.trace(String.format("Ignoring pools [%s] as they are in disabled state.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(disabledPools)));
logger.trace("Ignoring pools [{}] as they are in disabled state.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(disabledPools));
}
}
protected void logStartOfSearch(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, int returnUpTo,
boolean bypassStorageTypeCheck){
logger.trace(String.format("%s is looking for storage pools that match the VM's disk profile [%s], virtual machine profile [%s] and "
+ "deployment plan [%s]. Returning up to [%d] and bypassStorageTypeCheck [%s].", this.getClass().getSimpleName(), dskCh, vmProfile, plan, returnUpTo, bypassStorageTypeCheck));
logger.trace("[{}] is looking for storage pools that match the VM's disk profile [{}], virtual machine profile [{}] and "
+ "deployment plan [{}]. Returning up to [{}] and bypassStorageTypeCheck [{}].", this.getClass().getSimpleName(), dskCh, vmProfile, plan, returnUpTo, bypassStorageTypeCheck);
}
protected void logEndOfSearch(List<StoragePool> storagePoolList) {
logger.debug(String.format("%s is returning [%s] suitable storage pools [%s].", this.getClass().getSimpleName(), storagePoolList.size(),
Arrays.toString(storagePoolList.toArray())));
logger.debug("[{}] is returning [{}] suitable storage pools [{}].", this.getClass().getSimpleName(), storagePoolList.size(),
Arrays.toString(storagePoolList.toArray()));
}
}

View File

@ -230,8 +230,10 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
updateBuilder.setJobId(answer.getJobId());
updateBuilder.setLocalDownloadPath(answer.getDownloadPath());
updateBuilder.setInstallPath(answer.getInstallPath());
updateBuilder.setSize(answer.getTemplateSize());
updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize());
if (!VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) {
updateBuilder.setSize(answer.getTemplateSize());
updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize());
}
_templateStoreDao.update(tmpltStoreVO.getId(), updateBuilder);
// update size in vm_template table
VMTemplateVO tmlptUpdater = _templateDao.createForUpdate();
@ -241,8 +243,7 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
AsyncCompletionCallback<CreateCmdResult> caller = context.getParentCallback();
if (answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR ||
answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.ABANDONED || answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.UNKNOWN) {
if (VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) {
CreateCmdResult result = new CreateCmdResult(null, null);
result.setSuccess(false);
result.setResult(answer.getErrorString());
@ -285,19 +286,22 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
updateBuilder.setJobId(answer.getJobId());
updateBuilder.setLocalDownloadPath(answer.getDownloadPath());
updateBuilder.setInstallPath(answer.getInstallPath());
updateBuilder.setSize(answer.getTemplateSize());
updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize());
if (!VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) {
updateBuilder.setSize(answer.getTemplateSize());
updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize());
}
_volumeStoreDao.update(volStoreVO.getId(), updateBuilder);
// update size in volume table
VolumeVO volUpdater = volumeDao.createForUpdate();
volUpdater.setSize(answer.getTemplateSize());
volumeDao.update(obj.getId(), volUpdater);
if (!VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) {
VolumeVO volUpdater = volumeDao.createForUpdate();
volUpdater.setSize(answer.getTemplateSize());
volumeDao.update(obj.getId(), volUpdater);
}
}
AsyncCompletionCallback<CreateCmdResult> caller = context.getParentCallback();
if (answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR ||
answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.ABANDONED || answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.UNKNOWN) {
if (VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) {
CreateCmdResult result = new CreateCmdResult(null, null);
result.setSuccess(false);
result.setResult(answer.getErrorString());

View File

@ -83,6 +83,12 @@ public class CreateExtensionCmd extends BaseCmd {
description = "Details in key/value pairs using format details[i].keyname=keyvalue. Example: details[0].endpoint.url=urlvalue")
protected Map details;
@Parameter(name = ApiConstants.RESERVED_RESOURCE_DETAILS, type = CommandType.STRING,
description = "Resource detail names as comma separated string that should be reserved and not visible " +
"to end users",
since = "4.22.1")
protected String reservedResourceDetails;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -115,6 +121,10 @@ public class CreateExtensionCmd extends BaseCmd {
return convertDetailsToMap(details);
}
public String getReservedResourceDetails() {
return reservedResourceDetails;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -78,6 +78,12 @@ public class UpdateExtensionCmd extends BaseCmd {
"if false or not set, no action)")
private Boolean cleanupDetails;
@Parameter(name = ApiConstants.RESERVED_RESOURCE_DETAILS, type = CommandType.STRING,
description = "Resource detail names as comma separated string that should be reserved and not visible " +
"to end users",
since = "4.22.1")
protected String reservedResourceDetails;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -106,6 +112,10 @@ public class UpdateExtensionCmd extends BaseCmd {
return cleanupDetails;
}
public String getReservedResourceDetails() {
return reservedResourceDetails;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -216,6 +216,11 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
@Inject
AccountService accountService;
// Map of in-built extension names and their reserved resource details that shouldn't be accessible to end-users
protected static final Map<String, List<String>> INBUILT_RESERVED_RESOURCE_DETAILS = Map.of(
"proxmox", List.of("proxmox_vmid")
);
private ScheduledExecutorService extensionPathStateCheckExecutor;
protected String getDefaultExtensionRelativePath(String name) {
@ -563,6 +568,25 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
updateExtensionPathReady(extension, true);
}
protected void addInbuiltExtensionReservedResourceDetails(long extensionId, List<String> reservedResourceDetails) {
ExtensionVO vo = extensionDao.findById(extensionId);
if (vo == null || vo.isUserDefined()) {
return;
}
String lowerName = StringUtils.defaultString(vo.getName()).toLowerCase();
Optional<Map.Entry<String, List<String>>> match = INBUILT_RESERVED_RESOURCE_DETAILS.entrySet().stream()
.filter(e -> lowerName.contains(e.getKey().toLowerCase()))
.findFirst();
if (match.isPresent()) {
Set<String> existing = new HashSet<>(reservedResourceDetails);
for (String detailKey : match.get().getValue()) {
if (existing.add(detailKey)) {
reservedResourceDetails.add(detailKey);
}
}
}
}
@Override
public String getExtensionsPath() {
return externalProvisioner.getExtensionsPath();
@ -577,6 +601,7 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
String relativePath = cmd.getPath();
final Boolean orchestratorRequiresPrepareVm = cmd.isOrchestratorRequiresPrepareVm();
final String stateStr = cmd.getState();
final String reservedResourceDetails = cmd.getReservedResourceDetails();
ExtensionVO extensionByName = extensionDao.findByName(name);
if (extensionByName != null) {
throw new CloudRuntimeException("Extension by name already exists");
@ -624,6 +649,10 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM, String.valueOf(orchestratorRequiresPrepareVm),
false));
}
if (StringUtils.isNotBlank(reservedResourceDetails)) {
detailsVOList.add(new ExtensionDetailsVO(extension.getId(),
ApiConstants.RESERVED_RESOURCE_DETAILS, reservedResourceDetails, false));
}
if (CollectionUtils.isNotEmpty(detailsVOList)) {
extensionDetailsDao.saveDetails(detailsVOList);
}
@ -704,6 +733,7 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
final String stateStr = cmd.getState();
final Map<String, String> details = cmd.getDetails();
final Boolean cleanupDetails = cmd.isCleanupDetails();
final String reservedResourceDetails = cmd.getReservedResourceDetails();
final ExtensionVO extensionVO = extensionDao.findById(id);
if (extensionVO == null) {
throw new InvalidParameterValueException("Failed to find the extension");
@ -732,7 +762,8 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
throw new CloudRuntimeException(String.format("Failed to updated the extension: %s",
extensionVO.getName()));
}
updateExtensionsDetails(cleanupDetails, details, orchestratorRequiresPrepareVm, id);
updateExtensionsDetails(cleanupDetails, details, orchestratorRequiresPrepareVm, reservedResourceDetails,
id);
return extensionVO;
});
if (StringUtils.isNotBlank(stateStr)) {
@ -748,9 +779,11 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
return result;
}
protected void updateExtensionsDetails(Boolean cleanupDetails, Map<String, String> details, Boolean orchestratorRequiresPrepareVm, long id) {
protected void updateExtensionsDetails(Boolean cleanupDetails, Map<String, String> details,
Boolean orchestratorRequiresPrepareVm, String reservedResourceDetails, long id) {
final boolean needToUpdateAllDetails = Boolean.TRUE.equals(cleanupDetails) || MapUtils.isNotEmpty(details);
if (!needToUpdateAllDetails && orchestratorRequiresPrepareVm == null) {
if (!needToUpdateAllDetails && orchestratorRequiresPrepareVm == null &&
StringUtils.isBlank(reservedResourceDetails)) {
return;
}
if (needToUpdateAllDetails) {
@ -761,6 +794,9 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
hiddenDetails.put(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM,
String.valueOf(orchestratorRequiresPrepareVm));
}
if (StringUtils.isNotBlank(reservedResourceDetails)) {
hiddenDetails.put(ApiConstants.RESERVED_RESOURCE_DETAILS, reservedResourceDetails);
}
if (MapUtils.isNotEmpty(hiddenDetails)) {
hiddenDetails.forEach((key, value) -> detailsVOList.add(
new ExtensionDetailsVO(id, key, value, false)));
@ -775,15 +811,29 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
extensionDetailsDao.removeDetails(id);
}
} else {
ExtensionDetailsVO detailsVO = extensionDetailsDao.findDetail(id,
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM);
if (detailsVO == null) {
extensionDetailsDao.persist(new ExtensionDetailsVO(id,
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM,
String.valueOf(orchestratorRequiresPrepareVm), false));
} else if (Boolean.parseBoolean(detailsVO.getValue()) != orchestratorRequiresPrepareVm) {
detailsVO.setValue(String.valueOf(orchestratorRequiresPrepareVm));
extensionDetailsDao.update(detailsVO.getId(), detailsVO);
if (orchestratorRequiresPrepareVm != null) {
ExtensionDetailsVO detailsVO = extensionDetailsDao.findDetail(id,
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM);
if (detailsVO == null) {
extensionDetailsDao.persist(new ExtensionDetailsVO(id,
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM,
String.valueOf(orchestratorRequiresPrepareVm), false));
} else if (Boolean.parseBoolean(detailsVO.getValue()) != orchestratorRequiresPrepareVm) {
detailsVO.setValue(String.valueOf(orchestratorRequiresPrepareVm));
extensionDetailsDao.update(detailsVO.getId(), detailsVO);
}
}
if (StringUtils.isNotBlank(reservedResourceDetails)) {
ExtensionDetailsVO detailsVO = extensionDetailsDao.findDetail(id,
ApiConstants.RESERVED_RESOURCE_DETAILS);
if (detailsVO == null) {
extensionDetailsDao.persist(new ExtensionDetailsVO(id,
ApiConstants.RESERVED_RESOURCE_DETAILS,
reservedResourceDetails, false));
} else if (!reservedResourceDetails.equals(detailsVO.getValue())) {
detailsVO.setValue(reservedResourceDetails);
extensionDetailsDao.update(detailsVO.getId(), detailsVO);
}
}
}
}
@ -961,12 +1011,16 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
hiddenDetails = extensionDetails.second();
} else {
hiddenDetails = extensionDetailsDao.listDetailsKeyPairs(extension.getId(),
List.of(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM));
List.of(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM,
ApiConstants.RESERVED_RESOURCE_DETAILS));
}
if (hiddenDetails.containsKey(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM)) {
response.setOrchestratorRequiresPrepareVm(Boolean.parseBoolean(
hiddenDetails.get(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM)));
}
if (hiddenDetails.containsKey(ApiConstants.RESERVED_RESOURCE_DETAILS)) {
response.setReservedResourceDetails(hiddenDetails.get(ApiConstants.RESERVED_RESOURCE_DETAILS));
}
response.setObjectName(Extension.class.getSimpleName().toLowerCase());
return response;
}
@ -1605,6 +1659,24 @@ public class ExtensionsManagerImpl extends ManagerBase implements ExtensionsMana
return extensionDao.findById(extensionId);
}
@Override
public List<String> getExtensionReservedResourceDetails(long extensionId) {
ExtensionDetailsVO detailsVO = extensionDetailsDao.findDetail(extensionId,
ApiConstants.RESERVED_RESOURCE_DETAILS);
if (detailsVO == null || !StringUtils.isNotBlank(detailsVO.getValue())) {
return Collections.emptyList();
}
List<String> reservedDetails = new ArrayList<>();
String[] parts = detailsVO.getValue().split(",");
for (String part : parts) {
if (StringUtils.isNotBlank(part)) {
reservedDetails.add(part.trim());
}
}
addInbuiltExtensionReservedResourceDetails(extensionId, reservedDetails);
return reservedDetails;
}
@Override
public boolean start() {
long pathStateCheckInterval = PathStateCheckInterval.value();

View File

@ -94,4 +94,18 @@ public class CreateExtensionCmdTest {
setField(cmd, "details", details);
assertTrue(MapUtils.isNotEmpty(cmd.getDetails()));
}
@Test
public void getReservedResourceDetailsReturnsValueWhenSet() {
setField(cmd, "reservedResourceDetails", "detail1,detail2,detail3");
String result = cmd.getReservedResourceDetails();
assertEquals("detail1,detail2,detail3", result);
}
@Test
public void getReservedResourceDetailsReturnsNullWhenNotSet() {
setField(cmd, "reservedResourceDetails", null);
String result = cmd.getReservedResourceDetails();
assertNull(result);
}
}

View File

@ -26,6 +26,7 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.util.ReflectionTestUtils.setField;
import java.util.EnumSet;
import java.util.HashMap;
@ -134,6 +135,20 @@ public class UpdateExtensionCmdTest {
assertTrue(cmd.isCleanupDetails());
}
@Test
public void getReservedResourceDetailsReturnsValueWhenSet() {
setField(cmd, "reservedResourceDetails", "detail1,detail2,detail3");
String result = cmd.getReservedResourceDetails();
assertEquals("detail1,detail2,detail3", result);
}
@Test
public void getReservedResourceDetailsReturnsNullWhenNotSet() {
setField(cmd, "reservedResourceDetails", null);
String result = cmd.getReservedResourceDetails();
assertNull(result);
}
@Test
public void executeSetsExtensionResponseWhenManagerSucceeds() {
Extension extension = mock(Extension.class);

View File

@ -23,11 +23,13 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
@ -40,6 +42,7 @@ import static org.mockito.Mockito.when;
import java.io.File;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
@ -49,8 +52,6 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.user.AccountService;
import org.apache.cloudstack.acl.Role;
import org.apache.cloudstack.acl.RoleService;
import org.apache.cloudstack.acl.RoleType;
@ -85,9 +86,11 @@ import org.apache.cloudstack.framework.extensions.dao.ExtensionResourceMapDao;
import org.apache.cloudstack.framework.extensions.dao.ExtensionResourceMapDetailsDao;
import org.apache.cloudstack.framework.extensions.vo.ExtensionCustomActionDetailsVO;
import org.apache.cloudstack.framework.extensions.vo.ExtensionCustomActionVO;
import org.apache.cloudstack.framework.extensions.vo.ExtensionDetailsVO;
import org.apache.cloudstack.framework.extensions.vo.ExtensionResourceMapVO;
import org.apache.cloudstack.framework.extensions.vo.ExtensionVO;
import org.apache.cloudstack.utils.identity.ManagementServerNode;
import org.apache.commons.collections.CollectionUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -113,6 +116,7 @@ import com.cloud.dc.dao.ClusterDao;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.host.Host;
import com.cloud.host.dao.HostDao;
import com.cloud.host.dao.HostDetailsDao;
@ -122,6 +126,7 @@ import com.cloud.org.Cluster;
import com.cloud.serializer.GsonHelper;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.user.Account;
import com.cloud.user.AccountService;
import com.cloud.utils.Pair;
import com.cloud.utils.UuidUtils;
import com.cloud.utils.db.EntityManager;
@ -664,6 +669,8 @@ public class ExtensionsManagerImplTest {
when(cmd.getPath()).thenReturn(null);
when(cmd.isOrchestratorRequiresPrepareVm()).thenReturn(null);
when(cmd.getState()).thenReturn(null);
String reservedResourceDetails = "abc,xyz";
when(cmd.getReservedResourceDetails()).thenReturn(reservedResourceDetails);
when(extensionDao.findByName("ext1")).thenReturn(null);
when(extensionDao.persist(any())).thenAnswer(inv -> {
ExtensionVO extensionVO = inv.getArgument(0);
@ -671,11 +678,20 @@ public class ExtensionsManagerImplTest {
return extensionVO;
});
when(managementServerHostDao.listBy(any())).thenReturn(Collections.emptyList());
List<ExtensionDetailsVO> detailsList = new ArrayList<>();
doAnswer(inv -> {
List<ExtensionDetailsVO> detailsVO = inv.getArgument(0);
detailsList.addAll(detailsVO);
return null;
}).when(extensionDetailsDao).saveDetails(anyList());
Extension ext = extensionsManager.createExtension(cmd);
assertEquals("ext1", ext.getName());
verify(extensionDao).persist(any());
assertTrue(CollectionUtils.isNotEmpty(detailsList));
assertTrue(detailsList.stream()
.anyMatch(detail -> ApiConstants.RESERVED_RESOURCE_DETAILS.equals(detail.getName())
&& reservedResourceDetails.equals(detail.getValue())));
}
@Test
@ -938,14 +954,32 @@ public class ExtensionsManagerImplTest {
public void updateExtensionsDetails_SavesDetails_WhenDetailsProvided() {
long extensionId = 10L;
Map<String, String> details = Map.of("foo", "bar", "baz", "qux");
extensionsManager.updateExtensionsDetails(false, details, null, extensionId);
extensionsManager.updateExtensionsDetails(false, details, null, null, extensionId);
verify(extensionDetailsDao).saveDetails(any());
}
@Test
public void updateExtensionsDetails_PersistReservedDetail_WhenProvided() {
long extensionId = 10L;
when(extensionDetailsDao.persist(any())).thenReturn(mock(ExtensionDetailsVO.class));
extensionsManager.updateExtensionsDetails(false, null, null, "abc,xyz", extensionId);
verify(extensionDetailsDao).persist(any());
}
@Test
public void updateExtensionsDetails_UpdateReservedDetail_WhenProvided() {
long extensionId = 10L;
when(extensionDetailsDao.findDetail(anyLong(), eq(ApiConstants.RESERVED_RESOURCE_DETAILS)))
.thenReturn(mock(ExtensionDetailsVO.class));
when(extensionDetailsDao.update(anyLong(), any())).thenReturn(true);
extensionsManager.updateExtensionsDetails(false, null, null, "abc,xyz", extensionId);
verify(extensionDetailsDao).update(anyLong(), any());
}
@Test
public void updateExtensionsDetails_DoesNothing_WhenDetailsAndCleanupAreNull() {
long extensionId = 11L;
extensionsManager.updateExtensionsDetails(null, null, null, extensionId);
extensionsManager.updateExtensionsDetails(null, null, null, null, extensionId);
verify(extensionDetailsDao, never()).removeDetails(anyLong());
verify(extensionDetailsDao, never()).saveDetails(any());
}
@ -953,7 +987,7 @@ public class ExtensionsManagerImplTest {
@Test
public void updateExtensionsDetails_RemovesDetailsOnly_WhenCleanupIsTrue() {
long extensionId = 12L;
extensionsManager.updateExtensionsDetails(true, null, null, extensionId);
extensionsManager.updateExtensionsDetails(true, null, null, null, extensionId);
verify(extensionDetailsDao).removeDetails(extensionId);
verify(extensionDetailsDao, never()).saveDetails(any());
}
@ -961,7 +995,7 @@ public class ExtensionsManagerImplTest {
@Test
public void updateExtensionsDetails_PersistsOrchestratorFlag_WhenFlagIsNotNull() {
long extensionId = 13L;
extensionsManager.updateExtensionsDetails(false, null, true, extensionId);
extensionsManager.updateExtensionsDetails(false, null, true, null, extensionId);
verify(extensionDetailsDao).persist(any());
}
@ -970,7 +1004,7 @@ public class ExtensionsManagerImplTest {
long extensionId = 14L;
Map<String, String> details = Map.of("foo", "bar");
doThrow(CloudRuntimeException.class).when(extensionDetailsDao).saveDetails(any());
extensionsManager.updateExtensionsDetails(false, details, null, extensionId);
extensionsManager.updateExtensionsDetails(false, details, null, null, extensionId);
}
@Test
@ -1161,7 +1195,8 @@ public class ExtensionsManagerImplTest {
when(externalProvisioner.getExtensionPath("entry2.sh")).thenReturn("/some/path/entry2.sh");
Map<String, String> hiddenDetails = Map.of(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM, "false");
when(extensionDetailsDao.listDetailsKeyPairs(2L, List.of(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM)))
when(extensionDetailsDao.listDetailsKeyPairs(2L, List.of(
ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM, ApiConstants.RESERVED_RESOURCE_DETAILS)))
.thenReturn(hiddenDetails);
EnumSet<ApiConstants.ExtensionDetails> viewDetails = EnumSet.noneOf(ApiConstants.ExtensionDetails.class);
@ -2069,4 +2104,118 @@ public class ExtensionsManagerImplTest {
}
}
@Test
public void getExtensionReservedResourceDetailsReturnsEmptyListWhenDetailsNotFound() {
long extensionId = 1L;
when(extensionDetailsDao.findDetail(extensionId, ApiConstants.RESERVED_RESOURCE_DETAILS)).thenReturn(null);
List<String> result = extensionsManager.getExtensionReservedResourceDetails(extensionId);
assertNotNull(result);
assertTrue(result.isEmpty());
}
@Test
public void getExtensionReservedResourceDetailsReturnsEmptyListWhenValueIsBlank() {
long extensionId = 2L;
ExtensionDetailsVO detailsVO = mock(ExtensionDetailsVO.class);
when(detailsVO.getValue()).thenReturn(" ");
when(extensionDetailsDao.findDetail(extensionId, ApiConstants.RESERVED_RESOURCE_DETAILS)).thenReturn(detailsVO);
List<String> result = extensionsManager.getExtensionReservedResourceDetails(extensionId);
assertNotNull(result);
assertTrue(result.isEmpty());
}
@Test
public void getExtensionReservedResourceDetailsReturnsListOfTrimmedDetails() {
long extensionId = 3L;
ExtensionDetailsVO detailsVO = mock(ExtensionDetailsVO.class);
when(detailsVO.getValue()).thenReturn(" detail1 , detail2,detail3 ");
when(extensionDetailsDao.findDetail(extensionId, ApiConstants.RESERVED_RESOURCE_DETAILS)).thenReturn(detailsVO);
List<String> result = extensionsManager.getExtensionReservedResourceDetails(extensionId);
assertNotNull(result);
assertEquals(3, result.size());
assertEquals("detail1", result.get(0));
assertEquals("detail2", result.get(1));
assertEquals("detail3", result.get(2));
}
@Test
public void getExtensionReservedResourceDetailsHandlesEmptyPartsGracefully() {
long extensionId = 4L;
ExtensionDetailsVO detailsVO = mock(ExtensionDetailsVO.class);
when(detailsVO.getValue()).thenReturn("detail1,,detail2, ,detail3");
when(extensionDetailsDao.findDetail(extensionId, ApiConstants.RESERVED_RESOURCE_DETAILS)).thenReturn(detailsVO);
List<String> result = extensionsManager.getExtensionReservedResourceDetails(extensionId);
assertNotNull(result);
assertEquals(3, result.size());
assertEquals("detail1", result.get(0));
assertEquals("detail2", result.get(1));
assertEquals("detail3", result.get(2));
}
@Test
public void getExtensionReservedResourceDetailsReturnsEmptyListWhenSplitResultsInNoParts() {
long extensionId = 5L;
ExtensionDetailsVO detailsVO = mock(ExtensionDetailsVO.class);
when(detailsVO.getValue()).thenReturn(",");
when(extensionDetailsDao.findDetail(extensionId, ApiConstants.RESERVED_RESOURCE_DETAILS)).thenReturn(detailsVO);
List<String> result = extensionsManager.getExtensionReservedResourceDetails(extensionId);
assertNotNull(result);
assertTrue(result.isEmpty());
}
@Test
public void addInbuiltExtensionReservedResourceDetailsDoesNothingWhenExtensionNotFound() {
when(extensionDao.findById(1L)).thenReturn(null);
List<String> reservedResourceDetails = new ArrayList<>();
extensionsManager.addInbuiltExtensionReservedResourceDetails(1L, reservedResourceDetails);
assertTrue(reservedResourceDetails.isEmpty());
}
@Test
public void addInbuiltExtensionReservedResourceDetailsDoesNothingForUserDefinedExtension() {
ExtensionVO extension = mock(ExtensionVO.class);
when(extension.isUserDefined()).thenReturn(true);
when(extensionDao.findById(2L)).thenReturn(extension);
List<String> reservedResourceDetails = new ArrayList<>();
reservedResourceDetails.add("existing-detail");
extensionsManager.addInbuiltExtensionReservedResourceDetails(2L, reservedResourceDetails);
assertEquals(1, reservedResourceDetails.size());
assertTrue(reservedResourceDetails.contains("existing-detail"));
}
@Test
public void addInbuiltExtensionReservedResourceDetailsDoesNothingWhenNoMatchFound() {
ExtensionVO extension = mock(ExtensionVO.class);
when(extension.isUserDefined()).thenReturn(false);
when(extension.getName()).thenReturn("no-such-inbuilt-key-expected");
when(extensionDao.findById(3L)).thenReturn(extension);
List<String> reservedResourceDetails = new ArrayList<>();
extensionsManager.addInbuiltExtensionReservedResourceDetails(3L, reservedResourceDetails);
assertTrue(reservedResourceDetails.isEmpty());
}
@Test
public void addInbuiltExtensionReservedResourceDetailsAddedDetails() {
ExtensionVO extension = mock(ExtensionVO.class);
when(extension.isUserDefined()).thenReturn(false);
Map.Entry<String, List<String>> entry =
ExtensionsManagerImpl.INBUILT_RESERVED_RESOURCE_DETAILS.entrySet().iterator().next();
when(extension.getName()).thenReturn(entry.getKey());
when(extensionDao.findById(3L)).thenReturn(extension);
List<String> reservedResourceDetails = new ArrayList<>();
extensionsManager.addInbuiltExtensionReservedResourceDetails(3L, reservedResourceDetails);
assertFalse(reservedResourceDetails.isEmpty());
assertEquals(reservedResourceDetails.size(), entry.getValue().size());
assertTrue(reservedResourceDetails.containsAll(entry.getValue()));
}
}

View File

@ -23,12 +23,30 @@ import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
import com.cloud.utils.db.GenericDao;
import javax.annotation.Nullable;
public interface AsyncJobDao extends GenericDao<AsyncJobVO, Long> {
AsyncJobVO findInstancePendingAsyncJob(String instanceType, long instanceId);
List<AsyncJobVO> findInstancePendingAsyncJobs(String instanceType, Long accountId);
/**
* Finds async job matching the given parameters.
* Non-null parameters are added to search criteria.
* Returns the most recent job by creation date.
* <p>
* When searching by resourceId and resourceType, only one active job
* is expected per resource, so returning a single result is sufficient.
*
* @param id job ID
* @param resourceId resource ID (instanceId)
* @param resourceType resource type (instanceType)
* @return matching job or null
*/
@Nullable
AsyncJobVO findJob(Long id, Long resourceId, String resourceType);
AsyncJobVO findPseudoJob(long threadId, long msid);
void cleanupPseduoJobs(long msid);

View File

@ -22,6 +22,8 @@ import java.util.Date;
import java.util.List;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
import org.apache.cloudstack.jobs.JobInfo;
@ -45,6 +47,7 @@ public class AsyncJobDaoImpl extends GenericDaoBase<AsyncJobVO, Long> implements
private final SearchBuilder<AsyncJobVO> expiringUnfinishedAsyncJobSearch;
private final SearchBuilder<AsyncJobVO> expiringCompletedAsyncJobSearch;
private final SearchBuilder<AsyncJobVO> failureMsidAsyncJobSearch;
private final SearchBuilder<AsyncJobVO> byIdResourceIdResourceTypeSearch;
private final GenericSearchBuilder<AsyncJobVO, Long> asyncJobTypeSearch;
private final GenericSearchBuilder<AsyncJobVO, Long> pendingNonPseudoAsyncJobsSearch;
@ -95,6 +98,12 @@ public class AsyncJobDaoImpl extends GenericDaoBase<AsyncJobVO, Long> implements
failureMsidAsyncJobSearch.and("job_cmd", failureMsidAsyncJobSearch.entity().getCmd(), Op.IN);
failureMsidAsyncJobSearch.done();
byIdResourceIdResourceTypeSearch = createSearchBuilder();
byIdResourceIdResourceTypeSearch.and("id", byIdResourceIdResourceTypeSearch.entity().getId(), SearchCriteria.Op.EQ);
byIdResourceIdResourceTypeSearch.and("instanceId", byIdResourceIdResourceTypeSearch.entity().getInstanceId(), SearchCriteria.Op.EQ);
byIdResourceIdResourceTypeSearch.and("instanceType", byIdResourceIdResourceTypeSearch.entity().getInstanceType(), SearchCriteria.Op.EQ);
byIdResourceIdResourceTypeSearch.done();
asyncJobTypeSearch = createSearchBuilder(Long.class);
asyncJobTypeSearch.select(null, SearchCriteria.Func.COUNT, asyncJobTypeSearch.entity().getId());
asyncJobTypeSearch.and("job_info", asyncJobTypeSearch.entity().getCmdInfo(),Op.LIKE);
@ -140,6 +149,30 @@ public class AsyncJobDaoImpl extends GenericDaoBase<AsyncJobVO, Long> implements
return listBy(sc);
}
@Override
public AsyncJobVO findJob(Long id, Long resourceId, String resourceType) {
SearchCriteria<AsyncJobVO> sc = byIdResourceIdResourceTypeSearch.create();
if (id == null && resourceId == null && StringUtils.isBlank(resourceType)) {
logger.debug("findJob called with all null parameters");
return null;
}
if (id != null) {
sc.setParameters("id", id);
}
if (resourceId != null && StringUtils.isNotBlank(resourceType)) {
sc.setParameters("instanceType", resourceType);
sc.setParameters("instanceId", resourceId);
}
Filter filter = new Filter(AsyncJobVO.class, "created", false, 0L, 1L);
List<AsyncJobVO> result = searchIncludingRemoved(sc, filter, Boolean.FALSE, false);
if (CollectionUtils.isNotEmpty(result)) {
return result.get(0);
}
return null;
}
@Override
public AsyncJobVO findPseudoJob(long threadId, long msid) {
SearchCriteria<AsyncJobVO> sc = pseudoJobSearch.create();

View File

@ -83,6 +83,8 @@ Requires: (iptables-services or iptables)
Requires: rng-tools
Requires: (qemu-img or qemu-tools)
Requires: python3-pip
Requires: python3-six
Requires: python3-protobuf
Requires: python3-setuptools
Requires: (libgcrypt > 1.8.3 or libgcrypt20)
Group: System Environment/Libraries
@ -334,11 +336,11 @@ cp -r ui/dist/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui/
rm -f ${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui/config.json
ln -sf /etc/%{name}/ui/config.json ${RPM_BUILD_ROOT}%{_datadir}/%{name}-ui/config.json
# Package mysql-connector-python
wget -P ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl
wget -P ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel https://files.pythonhosted.org/packages/e9/93/4860cebd5ad3ff2664ad3c966490ccb46e3b88458b2095145bca11727ca4/setuptools-47.3.1-py3-none-any.whl
wget -P ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel https://files.pythonhosted.org/packages/32/27/1141a8232723dcb10a595cc0ce4321dcbbd5215300bf4acfc142343205bf/protobuf-3.19.6-py2.py3-none-any.whl
# Package mysql-connector-python (bundled to avoid dependency on external community repo)
# Version 8.0.31 is the last version supporting Python 3.6 (EL8)
wget -P ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel https://files.pythonhosted.org/packages/08/1f/42d74bae9dd6dcfec67c9ed0f3fa482b1ae5ac5f117ca82ab589ecb3ca19/mysql_connector_python-8.0.31-py2.py3-none-any.whl
# Version 8.3.0 supports Python 3.8 to 3.12 (EL9, EL10)
wget -P ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel https://files.pythonhosted.org/packages/53/ed/26a4b8cacb8852c6fd97d2d58a7f2591c41989807ea82bd8d9725a4e6937/mysql_connector_python-8.3.0-py2.py3-none-any.whl
chmod 440 ${RPM_BUILD_ROOT}%{_sysconfdir}/sudoers.d/%{name}-management
chmod 770 ${RPM_BUILD_ROOT}%{_localstatedir}/%{name}/mnt
@ -455,8 +457,13 @@ then
fi
%post management
# Install mysql-connector-python
pip3 install %{_datadir}/%{name}-management/setup/wheel/six-1.15.0-py2.py3-none-any.whl %{_datadir}/%{name}-management/setup/wheel/setuptools-47.3.1-py3-none-any.whl %{_datadir}/%{name}-management/setup/wheel/protobuf-3.19.6-py2.py3-none-any.whl %{_datadir}/%{name}-management/setup/wheel/mysql_connector_python-8.0.31-py2.py3-none-any.whl
# Install mysql-connector-python wheel
# Detect Python version to install compatible wheel
if python3 -c 'import sys; sys.exit(0 if sys.version_info >= (3, 7) else 1)'; then
pip3 install %{_datadir}/%{name}-management/setup/wheel/mysql_connector_python-8.3.0-py2.py3-none-any.whl
else
pip3 install %{_datadir}/%{name}-management/setup/wheel/mysql_connector_python-8.0.31-py2.py3-none-any.whl
fi
/usr/bin/systemctl enable cloudstack-management > /dev/null 2>&1 || true
/usr/bin/systemctl enable --now rngd > /dev/null 2>&1 || true

View File

@ -106,7 +106,6 @@ public class BareMetalTemplateAdapter extends TemplateAdapterBase implements Tem
}
}
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
return template;
}

View File

@ -35,7 +35,6 @@ import com.cloud.agent.properties.AgentPropertiesFileHandler;
public class KVMHABase {
protected Logger logger = LogManager.getLogger(getClass());
private long _timeout = 60000; /* 1 minutes */
protected static String s_heartBeatPath;
protected long _heartBeatUpdateTimeout = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.HEARTBEAT_UPDATE_TIMEOUT);
protected long _heartBeatUpdateFreq = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_FREQUENCY);
protected long _heartBeatUpdateMaxTries = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_HEARTBEAT_UPDATE_MAX_TRIES);

View File

@ -18,7 +18,7 @@ package com.cloud.hypervisor.kvm.resource;
import com.cloud.agent.properties.AgentProperties;
import com.cloud.agent.properties.AgentPropertiesFileHandler;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.ha.HighAvailabilityManager;
import com.cloud.utils.script.Script;
import org.libvirt.Connect;
import org.libvirt.LibvirtException;
@ -39,20 +39,15 @@ public class KVMHAMonitor extends KVMHABase implements Runnable {
private final String hostPrivateIp;
public KVMHAMonitor(HAStoragePool pool, String host, String scriptPath) {
public KVMHAMonitor(HAStoragePool pool, String host) {
if (pool != null) {
storagePool.put(pool.getPoolUUID(), pool);
}
hostPrivateIp = host;
configureHeartBeatPath(scriptPath);
rebootHostAndAlertManagementOnHeartbeatTimeout = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.REBOOT_HOST_AND_ALERT_MANAGEMENT_ON_HEARTBEAT_TIMEOUT);
}
private static synchronized void configureHeartBeatPath(String scriptPath) {
KVMHABase.s_heartBeatPath = scriptPath;
}
public void addStoragePool(HAStoragePool pool) {
synchronized (storagePool) {
storagePool.put(pool.getPoolUUID(), pool);
@ -86,8 +81,8 @@ public class KVMHAMonitor extends KVMHABase implements Runnable {
Set<String> removedPools = new HashSet<>();
for (String uuid : storagePool.keySet()) {
HAStoragePool primaryStoragePool = storagePool.get(uuid);
if (primaryStoragePool.getPool().getType() == StoragePoolType.NetworkFilesystem) {
checkForNotExistingPools(removedPools, uuid);
if (HighAvailabilityManager.LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(primaryStoragePool.getPool().getType())) {
checkForNotExistingLibvirtStoragePools(removedPools, uuid);
if (removedPools.contains(uuid)) {
continue;
}
@ -127,7 +122,7 @@ public class KVMHAMonitor extends KVMHABase implements Runnable {
return result;
}
private void checkForNotExistingPools(Set<String> removedPools, String uuid) {
private void checkForNotExistingLibvirtStoragePools(Set<String> removedPools, String uuid) {
try {
Connect conn = LibvirtConnection.getConnection();
StoragePool storage = conn.storagePoolLookupByUUIDString(uuid);

View File

@ -1065,11 +1065,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
throw new ConfigurationException("Unable to find patch.sh");
}
heartBeatPath = Script.findScript(kvmScriptsDir, "kvmheartbeat.sh");
if (heartBeatPath == null) {
throw new ConfigurationException("Unable to find kvmheartbeat.sh");
}
createVmPath = Script.findScript(storageScriptsDir, "createvm.sh");
if (createVmPath == null) {
throw new ConfigurationException("Unable to find the createvm.sh");
@ -1332,7 +1327,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final String[] info = NetUtils.getNetworkParams(privateNic);
kvmhaMonitor = new KVMHAMonitor(null, info[0], heartBeatPath);
kvmhaMonitor = new KVMHAMonitor(null, info[0]);
final Thread ha = new Thread(kvmhaMonitor);
ha.start();

View File

@ -48,7 +48,7 @@ public final class LibvirtCheckVMActivityOnStoragePoolCommandWrapper extends Com
KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(pool.getType(), pool.getUuid());
if (primaryPool.isPoolSupportHA()){
if (primaryPool.isPoolSupportHA()) {
final HAStoragePool nfspool = monitor.getStoragePool(pool.getUuid());
final KVMHAVMActivityChecker ha = new KVMHAVMActivityChecker(nfspool, command.getHost(), command.getVolumeList(), libvirtComputingResource.getVmActivityCheckPath(), command.getSuspectTimeInSeconds());
final Future<Boolean> future = executors.submit(ha);

View File

@ -45,11 +45,10 @@ public final class LibvirtCheckVirtualMachineCommandWrapper extends CommandWrapp
Integer vncPort = null;
if (state == PowerState.PowerOn) {
vncPort = libvirtComputingResource.getVncPort(conn, command.getVmName());
}
Domain vm = conn.domainLookupByName(command.getVmName());
if (state == PowerState.PowerOn && DomainInfo.DomainState.VIR_DOMAIN_PAUSED.equals(vm.getInfo().state)) {
return new CheckVirtualMachineAnswer(command, PowerState.PowerUnknown, vncPort);
Domain vm = conn.domainLookupByName(command.getVmName());
if (DomainInfo.DomainState.VIR_DOMAIN_PAUSED.equals(vm.getInfo().state)) {
return new CheckVirtualMachineAnswer(command, PowerState.PowerUnknown, vncPort);
}
}
return new CheckVirtualMachineAnswer(command, state, vncPort);

View File

@ -289,6 +289,7 @@ public class KVMStoragePoolManager {
if (pool instanceof LibvirtStoragePool) {
addPoolDetails(uuid, (LibvirtStoragePool) pool);
((LibvirtStoragePool) pool).setType(type);
}
return pool;
@ -390,6 +391,9 @@ public class KVMStoragePoolManager {
private synchronized KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map<String, String> details, boolean primaryStorage) {
StorageAdaptor adaptor = getStorageAdaptor(type);
KVMStoragePool pool = adaptor.createStoragePool(name, host, port, path, userInfo, type, details, primaryStorage);
if (pool instanceof LibvirtStoragePool) {
((LibvirtStoragePool) pool).setType(type);
}
// LibvirtStorageAdaptor-specific statement
if (pool.isPoolSupportHA() && primaryStorage) {

View File

@ -185,6 +185,8 @@ public class KVMStorageProcessor implements StorageProcessor {
private int incrementalSnapshotTimeout;
private int incrementalSnapshotRetryRebaseWait;
private static final String CHECKPOINT_XML_TEMP_DIR = "/tmp/cloudstack/checkpointXMLs";
private static final String BACKUP_XML_TEMP_DIR = "/tmp/cloudstack/backupXMLs";
@ -252,6 +254,7 @@ public class KVMStorageProcessor implements StorageProcessor {
_cmdsTimeout = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.CMDS_TIMEOUT) * 1000;
incrementalSnapshotTimeout = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.INCREMENTAL_SNAPSHOT_TIMEOUT) * 1000;
incrementalSnapshotRetryRebaseWait = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.INCREMENTAL_SNAPSHOT_RETRY_REBASE_WAIT) * 1000;
return true;
}
@ -2093,8 +2096,25 @@ public class KVMStorageProcessor implements StorageProcessor {
QemuImg qemuImg = new QemuImg(wait);
qemuImg.rebase(snapshotFile, parentSnapshotFile, PhysicalDiskFormat.QCOW2.toString(), false);
} catch (LibvirtException | QemuImgException e) {
logger.error("Exception while rebasing incremental snapshot [{}] due to: [{}].", snapshotName, e.getMessage(), e);
throw new CloudRuntimeException(e);
if (!StringUtils.contains(e.getMessage(), "Is another process using the image")) {
logger.error("Exception while rebasing incremental snapshot [{}] due to: [{}].", snapshotName, e.getMessage(), e);
throw new CloudRuntimeException(e);
}
retryRebase(snapshotName, wait, e, snapshotFile, parentSnapshotFile);
}
}
private void retryRebase(String snapshotName, int wait, Exception e, QemuImgFile snapshotFile, QemuImgFile parentSnapshotFile) {
logger.warn("Libvirt still has not released the lock, will wait [{}] milliseconds and try again later.", incrementalSnapshotRetryRebaseWait);
try {
Thread.sleep(incrementalSnapshotRetryRebaseWait);
QemuImg qemuImg = new QemuImg(wait);
qemuImg.rebase(snapshotFile, parentSnapshotFile, PhysicalDiskFormat.QCOW2.toString(), false);
} catch (LibvirtException | QemuImgException | InterruptedException ex) {
logger.error("Unable to rebase snapshot [{}].", snapshotName, ex);
CloudRuntimeException cre = new CloudRuntimeException(ex);
cre.addSuppressed(e);
throw cre;
}
}

View File

@ -31,6 +31,7 @@ import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
import com.cloud.agent.api.to.HostTO;
import com.cloud.agent.properties.AgentProperties;
import com.cloud.agent.properties.AgentPropertiesFileHandler;
import com.cloud.ha.HighAvailabilityManager;
import com.cloud.hypervisor.kvm.resource.KVMHABase.HAStoragePool;
import com.cloud.storage.Storage;
import com.cloud.storage.Storage.StoragePoolType;
@ -320,13 +321,24 @@ public class LibvirtStoragePool implements KVMStoragePool {
@Override
public boolean isPoolSupportHA() {
return type == StoragePoolType.NetworkFilesystem;
return HighAvailabilityManager.LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(type);
}
public String getHearthBeatPath() {
if (type == StoragePoolType.NetworkFilesystem) {
if (StoragePoolType.NetworkFilesystem.equals(type)) {
String kvmScriptsDir = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_SCRIPTS_DIR);
return Script.findScript(kvmScriptsDir, "kvmheartbeat.sh");
String scriptPath = Script.findScript(kvmScriptsDir, "kvmheartbeat.sh");
if (scriptPath == null) {
throw new CloudRuntimeException("Unable to find heartbeat script 'kvmheartbeat.sh' in directory: " + kvmScriptsDir);
}
return scriptPath;
} else if (StoragePoolType.SharedMountPoint.equals(type)) {
String kvmScriptsDir = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.KVM_SCRIPTS_DIR);
String scriptPath = Script.findScript(kvmScriptsDir, "kvmsmpheartbeat.sh");
if (scriptPath == null) {
throw new CloudRuntimeException("Unable to find heartbeat script 'kvmsmpheartbeat.sh' in directory: " + kvmScriptsDir);
}
return scriptPath;
}
return null;
}
@ -410,4 +422,8 @@ public class LibvirtStoragePool implements KVMStoragePool {
return true;
}
}
public void setType(StoragePoolType type) {
this.type = type;
}
}

View File

@ -0,0 +1,191 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.hypervisor.kvm.resource.wrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.libvirt.Connect;
import org.libvirt.Domain;
import org.libvirt.DomainInfo;
import org.libvirt.DomainInfo.DomainState;
import org.libvirt.LibvirtException;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.agent.api.CheckVirtualMachineAnswer;
import com.cloud.agent.api.CheckVirtualMachineCommand;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.vm.VirtualMachine.PowerState;
@RunWith(MockitoJUnitRunner.class)
public class LibvirtCheckVirtualMachineCommandWrapperTest {
private static final String VM_NAME = "i-2-3-VM";
@Mock
private LibvirtComputingResource libvirtComputingResource;
@Mock
private LibvirtUtilitiesHelper libvirtUtilitiesHelper;
@Mock
private Connect conn;
@Mock
private Domain domain;
private LibvirtCheckVirtualMachineCommandWrapper wrapper;
private CheckVirtualMachineCommand command;
@Before
public void setUp() throws LibvirtException {
wrapper = new LibvirtCheckVirtualMachineCommandWrapper();
command = new CheckVirtualMachineCommand(VM_NAME);
when(libvirtComputingResource.getLibvirtUtilitiesHelper()).thenReturn(libvirtUtilitiesHelper);
when(libvirtUtilitiesHelper.getConnectionByVmName(VM_NAME)).thenReturn(conn);
}
@Test
public void testExecuteVmPoweredOnReturnsStateAndVncPort() throws LibvirtException {
DomainInfo domainInfo = new DomainInfo();
domainInfo.state = DomainState.VIR_DOMAIN_RUNNING;
when(libvirtComputingResource.getVmState(conn, VM_NAME)).thenReturn(PowerState.PowerOn);
when(libvirtComputingResource.getVncPort(conn, VM_NAME)).thenReturn(5900);
when(conn.domainLookupByName(VM_NAME)).thenReturn(domain);
when(domain.getInfo()).thenReturn(domainInfo);
CheckVirtualMachineAnswer answer = (CheckVirtualMachineAnswer) wrapper.execute(command, libvirtComputingResource);
assertTrue(answer.getResult());
assertEquals(PowerState.PowerOn, answer.getState());
assertEquals(Integer.valueOf(5900), answer.getVncPort());
}
@Test
public void testExecuteVmPausedReturnsPowerUnknown() throws LibvirtException {
DomainInfo domainInfo = new DomainInfo();
domainInfo.state = DomainState.VIR_DOMAIN_PAUSED;
when(libvirtComputingResource.getVmState(conn, VM_NAME)).thenReturn(PowerState.PowerOn);
when(libvirtComputingResource.getVncPort(conn, VM_NAME)).thenReturn(5901);
when(conn.domainLookupByName(VM_NAME)).thenReturn(domain);
when(domain.getInfo()).thenReturn(domainInfo);
CheckVirtualMachineAnswer answer = (CheckVirtualMachineAnswer) wrapper.execute(command, libvirtComputingResource);
assertTrue(answer.getResult());
assertEquals(PowerState.PowerUnknown, answer.getState());
assertEquals(Integer.valueOf(5901), answer.getVncPort());
}
@Test
public void testExecuteVmPoweredOffReturnsStateWithNullVncPort() throws LibvirtException {
when(libvirtComputingResource.getVmState(conn, VM_NAME)).thenReturn(PowerState.PowerOff);
CheckVirtualMachineAnswer answer = (CheckVirtualMachineAnswer) wrapper.execute(command, libvirtComputingResource);
assertTrue(answer.getResult());
assertEquals(PowerState.PowerOff, answer.getState());
assertNull(answer.getVncPort());
}
@Test
public void testExecuteVmStateUnknownReturnsStateWithNullVncPort() throws LibvirtException {
when(libvirtComputingResource.getVmState(conn, VM_NAME)).thenReturn(PowerState.PowerUnknown);
CheckVirtualMachineAnswer answer = (CheckVirtualMachineAnswer) wrapper.execute(command, libvirtComputingResource);
assertTrue(answer.getResult());
assertEquals(PowerState.PowerUnknown, answer.getState());
assertNull(answer.getVncPort());
}
@Test
public void testExecuteVmPoweredOnWithNullVncPort() throws LibvirtException {
DomainInfo domainInfo = new DomainInfo();
domainInfo.state = DomainState.VIR_DOMAIN_RUNNING;
when(libvirtComputingResource.getVmState(conn, VM_NAME)).thenReturn(PowerState.PowerOn);
when(libvirtComputingResource.getVncPort(conn, VM_NAME)).thenReturn(null);
when(conn.domainLookupByName(VM_NAME)).thenReturn(domain);
when(domain.getInfo()).thenReturn(domainInfo);
CheckVirtualMachineAnswer answer = (CheckVirtualMachineAnswer) wrapper.execute(command, libvirtComputingResource);
assertTrue(answer.getResult());
assertEquals(PowerState.PowerOn, answer.getState());
assertNull(answer.getVncPort());
}
@Test
public void testExecuteLibvirtExceptionOnGetConnectionReturnsFailure() throws LibvirtException {
LibvirtException libvirtException = mock(LibvirtException.class);
when(libvirtException.getMessage()).thenReturn("Connection refused");
when(libvirtUtilitiesHelper.getConnectionByVmName(VM_NAME)).thenThrow(libvirtException);
CheckVirtualMachineAnswer answer = (CheckVirtualMachineAnswer) wrapper.execute(command, libvirtComputingResource);
assertFalse(answer.getResult());
assertEquals("Connection refused", answer.getDetails());
}
@Test
public void testExecuteLibvirtExceptionOnGetVncPortReturnsFailure() throws LibvirtException {
LibvirtException libvirtException = mock(LibvirtException.class);
when(libvirtException.getMessage()).thenReturn("VNC port error");
when(libvirtComputingResource.getVmState(conn, VM_NAME)).thenReturn(PowerState.PowerOn);
when(libvirtComputingResource.getVncPort(conn, VM_NAME)).thenThrow(libvirtException);
CheckVirtualMachineAnswer answer = (CheckVirtualMachineAnswer) wrapper.execute(command, libvirtComputingResource);
assertFalse(answer.getResult());
assertEquals("VNC port error", answer.getDetails());
}
@Test
public void testExecuteLibvirtExceptionOnDomainLookupReturnsFailure() throws LibvirtException {
LibvirtException libvirtException = mock(LibvirtException.class);
when(libvirtException.getMessage()).thenReturn("Domain not found");
when(libvirtComputingResource.getVmState(conn, VM_NAME)).thenReturn(PowerState.PowerOn);
when(libvirtComputingResource.getVncPort(conn, VM_NAME)).thenReturn(5900);
when(conn.domainLookupByName(VM_NAME)).thenThrow(libvirtException);
CheckVirtualMachineAnswer answer = (CheckVirtualMachineAnswer) wrapper.execute(command, libvirtComputingResource);
assertFalse(answer.getResult());
assertEquals("Domain not found", answer.getDetails());
}
@Test
public void testExecuteCallsGetLibvirtUtilitiesHelper() throws LibvirtException {
when(libvirtComputingResource.getVmState(conn, VM_NAME)).thenReturn(PowerState.PowerOff);
wrapper.execute(command, libvirtComputingResource);
verify(libvirtComputingResource).getLibvirtUtilitiesHelper();
verify(libvirtUtilitiesHelper).getConnectionByVmName(VM_NAME);
}
}

View File

@ -24,6 +24,8 @@ import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
@ -98,6 +100,51 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl {
return String.format("%s-%s", ACS_PREFIX, account.getUuid());
}
private void updateCannedPolicy(long storeId, Account account, String excludeBucket) {
List<BucketVO> buckets = _bucketDao.listByObjectStoreIdAndAccountId(storeId, account.getId());
String resources = buckets.stream()
.map(BucketVO::getName)
.filter(name -> !Objects.equals(name, excludeBucket))
.map(name -> "\"arn:aws:s3:::" + name + "/*\"")
.collect(Collectors.joining(",\n"));
String policy;
if (resources.isEmpty()) {
// Resource cannot be empty in a canned Policy so deny access to all resources if the user has no buckets
policy = " {\n" +
" \"Statement\": [\n" +
" {\n" +
" \"Action\": \"s3:*\",\n" +
" \"Effect\": \"Deny\",\n" +
" \"Resource\": [\"arn:aws:s3:::*\", \"arn:aws:s3:::*/*\"]\n" +
" }\n" +
" ],\n" +
" \"Version\": \"2012-10-17\"\n" +
" }";
} else {
policy = " {\n" +
" \"Statement\": [\n" +
" {\n" +
" \"Action\": \"s3:*\",\n" +
" \"Effect\": \"Allow\",\n" +
" \"Resource\": [" + resources + "]\n" +
" }\n" +
" ],\n" +
" \"Version\": \"2012-10-17\"\n" +
" }";
}
MinioAdminClient minioAdminClient = getMinIOAdminClient(storeId);
String policyName = getUserOrAccessKeyForAccount(account) + "-policy";
String userName = getUserOrAccessKeyForAccount(account);
try {
minioAdminClient.addCannedPolicy(policyName, policy);
minioAdminClient.setPolicy(userName, false, policyName);
} catch (NoSuchAlgorithmException | IOException | InvalidKeyException e) {
throw new CloudRuntimeException(e);
}
}
@Override
public Bucket createBucket(Bucket bucket, boolean objectLock) {
//ToDo Client pool mgmt
@ -125,33 +172,8 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl {
throw new CloudRuntimeException(e);
}
List<BucketVO> buckets = _bucketDao.listByObjectStoreIdAndAccountId(storeId, accountId);
StringBuilder resources_builder = new StringBuilder();
for(BucketVO exitingBucket : buckets) {
resources_builder.append("\"arn:aws:s3:::"+exitingBucket.getName()+"/*\",\n");
}
resources_builder.append("\"arn:aws:s3:::"+bucketName+"/*\"\n");
updateCannedPolicy(storeId, account,null);
String policy = " {\n" +
" \"Statement\": [\n" +
" {\n" +
" \"Action\": \"s3:*\",\n" +
" \"Effect\": \"Allow\",\n" +
" \"Principal\": \"*\",\n" +
" \"Resource\": ["+resources_builder+"]" +
" }\n" +
" ],\n" +
" \"Version\": \"2012-10-17\"\n" +
" }";
MinioAdminClient minioAdminClient = getMinIOAdminClient(storeId);
String policyName = getUserOrAccessKeyForAccount(account) + "-policy";
String userName = getUserOrAccessKeyForAccount(account);
try {
minioAdminClient.addCannedPolicy(policyName, policy);
minioAdminClient.setPolicy(userName, false, policyName);
} catch (Exception e) {
throw new CloudRuntimeException(e);
}
String accessKey = _accountDetailsDao.findDetail(accountId, MINIO_ACCESS_KEY).getValue();
String secretKey = _accountDetailsDao.findDetail(accountId, MINIO_SECRET_KEY).getValue();
ObjectStoreVO store = _storeDao.findById(storeId);
@ -183,6 +205,8 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl {
@Override
public boolean deleteBucket(BucketTO bucket, long storeId) {
String bucketName = bucket.getName();
long accountId = bucket.getAccountId();
Account account = _accountDao.findById(accountId);
MinioClient minioClient = getMinIOClient(storeId);
try {
if(!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
@ -197,6 +221,9 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl {
} catch (Exception e) {
throw new CloudRuntimeException(e);
}
updateCannedPolicy(storeId, account, bucketName);
return true;
}

View File

@ -129,10 +129,15 @@ public class MinIOObjectStoreDriverImplTest {
@Test
public void testDeleteBucket() throws Exception {
String bucketName = "test-bucket";
BucketTO bucket = new BucketTO(bucketName);
BucketVO bucketVO = new BucketVO(1L, 1L, 1L, bucketName, 1, false, false, false, null);
BucketTO bucket = new BucketTO(bucketVO);
when(accountDao.findById(1L)).thenReturn(account);
when(account.getUuid()).thenReturn(UUID.randomUUID().toString());
when(bucketDao.listByObjectStoreIdAndAccountId(anyLong(), anyLong())).thenReturn(new ArrayList<BucketVO>());
doReturn(minioClient).when(minioObjectStoreDriverImpl).getMinIOClient(anyLong());
when(minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())).thenReturn(true);
doNothing().when(minioClient).removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
doReturn(minioAdminClient).when(minioObjectStoreDriverImpl).getMinIOAdminClient(anyLong());
boolean success = minioObjectStoreDriverImpl.deleteBucket(bucket, 1L);
assertTrue(success);
verify(minioClient, times(1)).bucketExists(any());

View File

@ -27,6 +27,7 @@ import java.util.UUID;
import javax.inject.Inject;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.ha.HighAvailabilityManager;
import com.cloud.storage.VolumeVO;
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
@ -587,7 +588,7 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
@Override
public boolean isStorageSupportHA(StoragePoolType type) {
return StoragePoolType.NetworkFilesystem == type;
return type != null && HighAvailabilityManager.LIBVIRT_STORAGE_POOL_TYPES_WITH_HA_SUPPORT.contains(type);
}
@Override

View File

@ -384,6 +384,7 @@ public class PrimeraAdapter implements ProviderAdapter {
// Online copy configuration: immediate clone with deduplication and compression
parms.setOnline(true);
parms.setDestCPG(cpg);
parms.setSnapCPG(snapCpg);
parms.setTpvv(false);
parms.setReduce(true);
logger.debug("PrimeraAdapter: Configuring online copy - destination CPG: '{}', deduplication enabled, thin provisioning disabled", cpg);

View File

@ -0,0 +1,218 @@
#!/bin/bash
# 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.
help() {
printf "Usage: $0
-i identifier (required for CLI compatibility; value ignored by local-only heartbeat)
-p path (required for CLI compatibility; value ignored by local-only heartbeat)
-m mount point (local path where heartbeat will be written)
-h host (host IP/name to include in heartbeat filename)
-r write/read hb log (read-check mode)
-c cleanup (trigger emergency reboot)
-t interval between read hb log\n"
exit 1
}
#set -x
NfsSvrIP=
NfsSvrPath=
MountPoint=
HostIP=
interval=
rflag=0
cflag=0
while getopts 'i:p:m:h:t:rc' OPTION
do
case $OPTION in
i)
NfsSvrIP="$OPTARG"
;; # retained for CLI compatibility but unused for this script
p)
NfsSvrPath="$OPTARG"
;; # retained for CLI compatibility but unused for this script
m)
MountPoint="$OPTARG"
;;
h)
HostIP="$OPTARG"
;;
r)
rflag=1
;;
t)
interval="$OPTARG"
;;
c)
cflag=1
;;
*)
help
;;
esac
done
# For heartbeat we require a mountpoint
if [ -z "$MountPoint" ]
then
echo "Mount point (-m) is required"
help
fi
# Validate mount point exists, is (if possible) a mounted filesystem, and is writable
if [ ! -d "$MountPoint" ]; then
echo "Mount point directory does not exist: $MountPoint" >&2
exit 1
fi
# If the 'mountpoint' utility is available, ensure this is an actual mount
if command -v mountpoint >/dev/null 2>&1; then
if ! mountpoint -q "$MountPoint"; then
echo "Mount point is not a mounted filesystem: $MountPoint" >&2
exit 1
fi
fi
# Ensure the mount point is writable
if [ ! -w "$MountPoint" ]; then
echo "Mount point is not writable: $MountPoint" >&2
exit 1
fi
#delete VMs on this mountpoint (best-effort)
deleteVMs() {
local mountPoint=$1
# ensure it ends with a single trailing slash
mountPoint="${mountPoint%/}/"
vmPids=$(ps aux | grep qemu | grep "$mountPoint" | awk '{print $2}' 2> /dev/null)
if [ -z "$vmPids" ]
then
return
fi
for pid in $vmPids
do
kill -9 $pid &> /dev/null
done
}
#checking is there the mount point present under $MountPoint?
if grep -q "^[^ ]\+ $MountPoint " /proc/mounts
then
# mount exists; nothing to do here; keep for compatibility with original flow
:
else
# mount point not present
# if not in read-check mode, consider deleting VMs similar to original behavior
if [ "$rflag" == "0" ]
then
deleteVMs $MountPoint
fi
fi
hbFolder="$MountPoint/KVMHA"
hbFile="$hbFolder/hb-$HostIP"
write_hbLog() {
#write the heart beat log
stat "$hbFile" &> /dev/null
if [ $? -gt 0 ]
then
# create a new one
mkdir -p "$hbFolder" &> /dev/null
# touch will be done by atomic write below; ensure folder is writable
if [ ! -w "$hbFolder" ]; then
printf "Folder not writable: $hbFolder" >&2
return 2
fi
fi
timestamp=$(date +%s)
# Write atomically to avoid partial writes (write to tmp then mv)
tmpfile="${hbFile}.$$"
printf "%s\n" "$timestamp" > "$tmpfile" 2>/dev/null
if [ $? -ne 0 ]; then
printf "Failed to write heartbeat to $tmpfile" >&2
return 2
fi
mv -f "$tmpfile" "$hbFile" 2>/dev/null
return $?
}
check_hbLog() {
hb_diff=0
if [ ! -f "$hbFile" ]; then
# signal large difference if file missing
hb_diff=999999
return 1
fi
now=$(date +%s)
hb=$(cat "$hbFile" 2>/dev/null)
if [ -z "$hb" ]; then
hb_diff=999998
return 1
fi
diff=`expr $now - $hb 2>/dev/null`
if [ $? -ne 0 ]
then
hb_diff=999997
return 1
fi
if [ -z "$interval" ]; then
# if no interval provided, consider 0 as success
if [ $diff -gt 0 ]; then
hb_diff=$diff
return 1
else
hb_diff=0
return 0
fi
fi
if [ $diff -gt $interval ]
then
hb_diff=$diff
return 1
fi
hb_diff=0
return 0
}
if [ "$rflag" == "1" ]
then
check_hbLog
status=$?
diff="${hb_diff:-0}"
if [ $status -eq 0 ]
then
echo "=====> ALIVE <====="
else
echo "=====> Considering host as DEAD because last write on [$hbFile] was [$diff] seconds ago, but the max interval is [$interval] <======"
fi
exit 0
elif [ "$cflag" == "1" ]
then
/usr/bin/logger -t heartbeat "kvmsmpheartbeat.sh will reboot system because it was unable to write the heartbeat to the storage."
sync &
sleep 5
echo b > /proc/sysrq-trigger
exit $?
else
write_hbLog
exit $?
fi

View File

@ -45,7 +45,6 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.response.AccountResponse;
import org.apache.cloudstack.api.response.AsyncJobResponse;
import org.apache.cloudstack.api.response.BackupOfferingResponse;
import org.apache.cloudstack.api.response.BackupScheduleResponse;
import org.apache.cloudstack.api.response.DiskOfferingResponse;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.DomainRouterResponse;
@ -76,7 +75,6 @@ import org.apache.cloudstack.api.response.VpcOfferingResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.backup.BackupOffering;
import org.apache.cloudstack.backup.BackupRepository;
import org.apache.cloudstack.backup.BackupSchedule;
import org.apache.cloudstack.backup.dao.BackupDao;
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
import org.apache.cloudstack.backup.dao.BackupRepositoryDao;
@ -2302,6 +2300,10 @@ public class ApiDBUtils {
return s_accountService.isAdmin(account.getId());
}
public static Account getSystemAccount() {
return s_accountService.getSystemAccount();
}
public static List<ResourceTagJoinVO> listResourceTagViewByResourceUUID(String resourceUUID, ResourceObjectType resourceType) {
return s_tagJoinDao.listBy(resourceUUID, resourceType);
}
@ -2310,10 +2312,6 @@ public class ApiDBUtils {
return s_resourceIconDao.findByResourceUuid(resourceUUID, resourceType);
}
public static BackupScheduleResponse newBackupScheduleResponse(BackupSchedule schedule) {
return s_backupScheduleDao.newBackupScheduleResponse(schedule);
}
public static BackupOfferingResponse newBackupOfferingResponse(BackupOffering offering) {
BackupRepository repository = s_backupRepositoryDao.findByUuid(offering.getExternalId());
Boolean crossZoneInstanceCreationEnabled = repository != null ? Boolean.TRUE.equals(repository.crossZoneInstanceCreationEnabled()) : false;

View File

@ -32,6 +32,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.Consumer;
@ -39,10 +40,6 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import com.cloud.domain.dao.DomainDao;
import com.cloud.user.AccountVO;
import com.cloud.user.ApiKeyPairState;
import com.cloud.user.dao.AccountDao;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.acl.RoleVO;
@ -53,6 +50,7 @@ import org.apache.cloudstack.affinity.AffinityGroup;
import org.apache.cloudstack.affinity.AffinityGroupResponse;
import org.apache.cloudstack.annotation.AnnotationService;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiConstants.DomainDetails;
import org.apache.cloudstack.api.ApiConstants.HostDetails;
@ -217,6 +215,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.framework.jobs.AsyncJob;
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao;
import org.apache.cloudstack.gui.theme.GuiThemeJoin;
import org.apache.cloudstack.management.ManagementServerHost;
import org.apache.cloudstack.network.BgpPeerVO;
@ -264,6 +263,7 @@ import com.cloud.api.query.vo.NetworkOfferingJoinVO;
import com.cloud.api.query.vo.ProjectAccountJoinVO;
import com.cloud.api.query.vo.ProjectInvitationJoinVO;
import com.cloud.api.query.vo.ProjectJoinVO;
import com.cloud.api.query.ResourceIdSupport;
import com.cloud.api.query.vo.ResourceTagJoinVO;
import com.cloud.api.query.vo.SecurityGroupJoinVO;
import com.cloud.api.query.vo.ServiceOfferingJoinVO;
@ -304,6 +304,7 @@ import com.cloud.dc.dao.ASNumberRangeDao;
import com.cloud.dc.dao.VlanDetailsDao;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.event.Event;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
@ -421,11 +422,14 @@ import com.cloud.tags.dao.ResourceTagDao;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.ApiKeyPairState;
import com.cloud.user.SSHKeyPair;
import com.cloud.user.User;
import com.cloud.user.UserAccount;
import com.cloud.user.UserData;
import com.cloud.user.UserStatisticsVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDataDao;
import com.cloud.user.dao.UserStatisticsDao;
import com.cloud.uservm.UserVm;
@ -458,7 +462,7 @@ import com.cloud.vm.snapshot.dao.VMSnapshotDao;
import sun.security.x509.X509CertImpl;
public class ApiResponseHelper implements ResponseGenerator {
public class ApiResponseHelper implements ResponseGenerator, ResourceIdSupport {
protected Logger logger = LogManager.getLogger(ApiResponseHelper.class);
private static final DecimalFormat s_percentFormat = new DecimalFormat("##.##");
@ -542,6 +546,8 @@ public class ApiResponseHelper implements ResponseGenerator {
Site2SiteVpnManager site2SiteVpnManager;
@Inject
ResourceIconManager resourceIconManager;
@Inject
AsyncJobDao asyncJobDao;
public static String getPrettyDomainPath(String path) {
if (path == null) {
@ -814,7 +820,7 @@ public class ApiResponseHelper implements ResponseGenerator {
if (mapCapabilities != null) {
String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString());
Boolean supportsStorageSystemSnapshots = new Boolean(value);
boolean supportsStorageSystemSnapshots = Boolean.getBoolean(value);
if (supportsStorageSystemSnapshots) {
return DataStoreRole.Primary;
@ -2335,16 +2341,26 @@ public class ApiResponseHelper implements ResponseGenerator {
@Override
public AsyncJobResponse queryJobResult(final QueryAsyncJobResultCmd cmd) {
final Account caller = CallContext.current().getCallingAccount();
ApiCommandResourceType resourceType = getResourceType(cmd.getResourceType());
String resourceTypeName = Optional.ofNullable(resourceType).map(ApiCommandResourceType::name).orElse(null);
final AsyncJob job = _entityMgr.findByIdIncludingRemoved(AsyncJob.class, cmd.getId());
if (job == null) {
throw new InvalidParameterValueException("Unable to find a job by id " + cmd.getId());
Long resourceId = getResourceId(resourceType, cmd.getResourceId());
Long jobId = cmd.getId();
if (jobId == null && resourceId == null) {
throw new InvalidParameterValueException("Expected parameter job id or parameters resource type and resource id");
}
final AsyncJob job = asyncJobDao.findJob(jobId, resourceId, resourceTypeName);
if (job == null) {
throw new InvalidParameterValueException("Unable to find a job by id " + jobId + " resource type "
+ cmd.getResourceType() + " resource id " + cmd.getResourceId());
}
jobId = job.getId();
final User userJobOwner = _accountMgr.getUserIncludingRemoved(job.getUserId());
final Account jobOwner = _accountMgr.getAccount(userJobOwner.getAccountId());
final Account caller = CallContext.current().getCallingAccount();
//check permissions
if (_accountMgr.isNormalUser(caller.getId())) {
//regular users can see only jobs they own
@ -2355,7 +2371,7 @@ public class ApiResponseHelper implements ResponseGenerator {
_accountMgr.checkAccess(caller, null, true, jobOwner);
}
return createAsyncJobResponse(_jobMgr.queryJob(cmd.getId(), true));
return createAsyncJobResponse(_jobMgr.queryJob(jobId, true));
}
public AsyncJobResponse createAsyncJobResponse(AsyncJob job) {
@ -5104,7 +5120,25 @@ public class ApiResponseHelper implements ResponseGenerator {
@Override
public BackupScheduleResponse createBackupScheduleResponse(BackupSchedule schedule) {
return ApiDBUtils.newBackupScheduleResponse(schedule);
BackupScheduleResponse response = new BackupScheduleResponse();
response.setId(schedule.getUuid());
response.setIntervalType(schedule.getScheduleType());
response.setSchedule(schedule.getSchedule());
response.setTimezone(schedule.getTimezone());
response.setMaxBackups(schedule.getMaxBackups());
if (schedule.getQuiesceVM() != null) {
response.setQuiesceVM(schedule.getQuiesceVM());
}
VMInstanceVO vm = ApiDBUtils.findVMInstanceById(schedule.getVmId());
if (vm != null) {
response.setVmId(vm.getUuid());
response.setVmName(vm.getHostName());
}
response.setObjectName("backupschedule");
return response;
}
@Override
@ -5823,4 +5857,14 @@ protected Map<String, ResourceIcon> getResourceIconsUsingOsCategory(List<Templat
response.setResponses(permissionResponses);
return response;
}
@Override
public EntityManager getEntityManager() {
return _entityMgr;
}
@Override
public AccountManager getAccountManager() {
return _accountMgr;
}
}

View File

@ -31,7 +31,6 @@ import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -375,7 +374,7 @@ import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.vm.dao.VMInstanceDetailsDao;
@Component
public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements QueryService, Configurable {
public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements QueryService, Configurable, ResourceIdSupport {
private static final String ID_FIELD = "id";
@ -911,26 +910,14 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
Integer entryTime = cmd.getEntryTime();
Integer duration = cmd.getDuration();
Long startId = cmd.getStartId();
final String resourceUuid = cmd.getResourceId();
final String resourceTypeStr = cmd.getResourceType();
final String resourceUuid = getResourceUuid(cmd.getResourceId());
final ApiCommandResourceType resourceType = getResourceType(cmd.getResourceType());
final String stateStr = cmd.getState();
ApiCommandResourceType resourceType = null;
Long resourceId = null;
if (resourceTypeStr != null) {
resourceType = ApiCommandResourceType.fromString(resourceTypeStr);
if (resourceType == null) {
throw new InvalidParameterValueException(String.format("Invalid %s", ApiConstants.RESOURCE_TYPE));
}
}
if (resourceUuid != null) {
if (resourceTypeStr == null) {
if (resourceType == null) {
throw new InvalidParameterValueException(String.format("%s parameter must be used with %s parameter", ApiConstants.RESOURCE_ID, ApiConstants.RESOURCE_TYPE));
}
try {
UUID.fromString(resourceUuid);
} catch (IllegalArgumentException ex) {
throw new InvalidParameterValueException(String.format("Invalid %s", ApiConstants.RESOURCE_ID));
}
Object object = entityManager.findByUuidIncludingRemoved(resourceType.getAssociatedClass(), resourceUuid);
if (object instanceof InternalIdentity) {
resourceId = ((InternalIdentity)object).getId();
@ -3276,6 +3263,18 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
sc.setParameters("executingMsid", msHost.getMsid());
}
if (cmd.getResourceType() != null) {
ApiCommandResourceType resourceType = getResourceType(cmd.getResourceType());
sc.addAnd("instanceType", SearchCriteria.Op.EQ, resourceType.toString());
final String resourceId = getResourceUuid(cmd.getResourceId());
if (resourceId != null) {
sc.addAnd("instanceUuid", SearchCriteria.Op.EQ, resourceId);
}
} else if (cmd.getResourceId() != null) {
throw new InvalidParameterValueException(String.format("%s parameter must be used with %s parameter", ApiConstants.RESOURCE_ID, ApiConstants.RESOURCE_TYPE));
}
return _jobJoinDao.searchAndCount(sc, searchFilter);
}
@ -6363,4 +6362,14 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
return new ConfigKey<?>[] {AllowUserViewDestroyedVM, UserVMDeniedDetails, UserVMReadOnlyDetails, SortKeyAscending,
AllowUserViewAllDomainAccounts, AllowUserViewAllDataCenters, SharePublicTemplatesWithOtherDomains, ReturnVmStatsOnVmList};
}
@Override
public EntityManager getEntityManager() {
return entityManager;
}
@Override
public AccountManager getAccountManager() {
return accountMgr;
}
}

View File

@ -0,0 +1,123 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.api.query;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.utils.db.EntityManager;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.context.CallContext;
import org.apache.commons.lang3.StringUtils;
import java.util.Optional;
import java.util.UUID;
import static org.apache.cloudstack.acl.SecurityChecker.AccessType;
/**
* Support interface for converting resource UUIDs to internal IDs
* with validation and access control.
*
* @author mprokopchuk
*/
public interface ResourceIdSupport {
EntityManager getEntityManager();
AccountManager getAccountManager();
/**
* Converts resource UUID to internal database ID with access control checks.
*
* @param resourceType type of the resource
* @param resourceUuid UUID of the resource
* @return internal resource ID or null if parameters are null
* @throws InvalidParameterValueException if only one parameter provided or resource not found
*/
default Long getResourceId(ApiCommandResourceType resourceType, String resourceUuid) {
String uuid = getResourceUuid(resourceUuid);
if (resourceType == null && uuid == null) {
return null;
} else if ((resourceType == null) ^ (uuid == null)) {
throw new InvalidParameterValueException(String.format("Both %s and %s required",
ApiConstants.RESOURCE_ID, ApiConstants.RESOURCE_TYPE));
}
Object object = getEntityManager().findByUuidIncludingRemoved(resourceType.getAssociatedClass(), resourceUuid);
if (!(object instanceof InternalIdentity)) {
throw new InvalidParameterValueException(String.format("Invalid %s", ApiConstants.RESOURCE_ID));
}
Long resourceId = ((InternalIdentity) object).getId();
Account caller = CallContext.current().getCallingAccount();
boolean isRootAdmin = getAccountManager().isRootAdmin(caller.getId());
if (!isRootAdmin && object instanceof ControlledEntity) {
ControlledEntity entity = (ControlledEntity) object;
boolean sameOwner = entity.getAccountId() == caller.getId();
getAccountManager().checkAccess(caller, AccessType.ListEntry, sameOwner, entity);
}
return resourceId;
}
/**
* Parses and validates resource type string.
*
* @param resourceType resource type as string
* @return parsed resource type or null if not provided
* @throws InvalidParameterValueException if provided type is invalid
*/
default ApiCommandResourceType getResourceType(String resourceType) {
Optional<String> resourceTypeOpt = Optional.ofNullable(resourceType).filter(StringUtils::isNotBlank);
// return null if resource type was not provided
if (resourceTypeOpt.isEmpty()) {
return null;
}
// return value or throw exception if provided resource type is invalid
return resourceTypeOpt
.map(ApiCommandResourceType::fromString)
.orElseThrow(() -> new InvalidParameterValueException(String.format("Invalid %s",
ApiConstants.RESOURCE_TYPE)));
}
/**
* Validates resource UUID format.
*
* @param resourceUuid UUID string to validate
* @return validated UUID or null if not provided
* @throws InvalidParameterValueException if UUID format is invalid
*/
default String getResourceUuid(String resourceUuid) {
if (StringUtils.isBlank(resourceUuid)) {
return null;
}
try {
UUID.fromString(resourceUuid);
} catch (IllegalArgumentException ex) {
throw new InvalidParameterValueException(String.format("Invalid %s", ApiConstants.RESOURCE_ID));
}
return resourceUuid;
}
}

View File

@ -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,8 +33,6 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import com.cloud.gpu.dao.VgpuProfileDao;
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;
@ -49,6 +46,7 @@ import org.apache.cloudstack.api.response.SecurityGroupResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VnfNicResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.extension.ExtensionHelper;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.query.QueryService;
import org.apache.cloudstack.vm.lease.VMLeaseManager;
@ -61,11 +59,13 @@ 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.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;
@ -94,7 +94,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
@ -128,6 +127,8 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
private VgpuProfileDao vgpuProfileDao;
@Inject
VMTemplateDao vmTemplateDao;
@Inject
ExtensionHelper extensionHelper;
private final SearchBuilder<UserVmJoinVO> VmDetailSearch;
private final SearchBuilder<UserVmJoinVO> activeVmByIsoSearch;
@ -461,7 +462,16 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
// Remove deny listed settings if user is not admin
if (caller.getType() != Account.Type.ADMIN) {
String[] userVmSettingsToHide = QueryService.UserVMDeniedDetails.value().split(",");
List<String> userVmSettingsToHide = new ArrayList<>();
String[] parts = QueryService.UserVMDeniedDetails.value().split(",");
if (parts.length > 0) {
Collections.addAll(userVmSettingsToHide, parts);
}
if (userVm.getTemplateExtensionId() != null) {
userVmSettingsToHide.addAll(extensionHelper.getExtensionReservedResourceDetails(
userVm.getTemplateExtensionId()));
}
for (String key : userVmSettingsToHide) {
resourceDetails.remove(key.trim());
}
@ -521,7 +531,6 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
return userVmResponse;
}
private long computeLeaseDurationFromExpiryDate(Date created, Date leaseExpiryDate) {
LocalDate createdDate = created.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate expiryDate = leaseExpiryDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
@ -557,7 +566,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
/**
* The resulting Response attempts to be in line with what is returned from
* @see com.cloud.api.ApiResponseHelper#createNicResponse(Nic)
* @see com.cloud.api.ApiResponseHelper#{code}createNicResponse(Nic){code}
*/
@Override
public UserVmResponse setUserVmResponse(ResponseView view, UserVmResponse userVmData, UserVmJoinVO uvo) {

View File

@ -207,6 +207,9 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
@Column(name = "template_format")
private Storage.ImageFormat templateFormat;
@Column(name = "template_extension_id")
private Long templateExtensionId;
@Column(name = "password_enabled")
private boolean passwordEnabled;
@ -712,6 +715,10 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
return templateFormat;
}
public Long getTemplateExtensionId() {
return templateExtensionId;
}
public boolean isPasswordEnabled() {
return passwordEnabled;
}

View File

@ -132,6 +132,7 @@ import org.apache.cloudstack.region.PortableIpVO;
import org.apache.cloudstack.region.Region;
import org.apache.cloudstack.region.RegionVO;
import org.apache.cloudstack.region.dao.RegionDao;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
@ -271,6 +272,7 @@ import com.cloud.org.Grouping;
import com.cloud.org.Grouping.AllocationState;
import com.cloud.projects.Project;
import com.cloud.projects.ProjectManager;
import com.cloud.resourcelimit.CheckedReservation;
import com.cloud.server.ManagementService;
import com.cloud.service.ServiceOfferingDetailsVO;
import com.cloud.service.ServiceOfferingVO;
@ -415,6 +417,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
@Inject
ResourceLimitService _resourceLimitMgr;
@Inject
ReservationDao reservationDao;
@Inject
ProjectManager _projectMgr;
@Inject
NetworkOfferingServiceMapDao _ntwkOffServiceMapDao;
@ -653,7 +657,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
protected void populateConfigKeysAllowedOnlyForDefaultAdmin() {
configKeysAllowedOnlyForDefaultAdmin.add(AccountManagerImpl.listOfRoleTypesAllowedForOperationsOfSameRoleType.key());
configKeysAllowedOnlyForDefaultAdmin.add(AccountManagerImpl.allowOperationsOnUsersInSameAccount.key());
configKeysAllowedOnlyForDefaultAdmin.add(VirtualMachineManager.SystemVmEnableUserData.key());
configKeysAllowedOnlyForDefaultAdmin.add(ConsoleProxyManager.ConsoleProxyVmUserData.key());
configKeysAllowedOnlyForDefaultAdmin.add(SecondaryStorageVmManager.SecondaryStorageVmUserData.key());
@ -1002,7 +1005,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
String hypervisors = _configDao.getValue(hypervisorListConfigName);
if (Arrays.asList(hypervisors.split(",")).contains(previousValue)) {
hypervisors = hypervisors.replace(previousValue, newValue);
logger.info("Updating the hypervisor list configuration '{}}' to match the new custom hypervisor display name",
logger.info("Updating the hypervisor list configuration '{}' to match the new custom hypervisor display name",
hypervisorListConfigName);
_configDao.update(hypervisorListConfigName, hypervisors);
}
@ -5512,22 +5515,20 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
throw new InvalidParameterValueException("Gateway, netmask and zoneId have to be passed in for virtual and direct untagged networks");
}
if (forVirtualNetwork) {
if (vlanOwner != null) {
final long accountIpRange = NetUtils.ip2Long(endIP) - NetUtils.ip2Long(startIP) + 1;
// check resource limits
_resourceLimitMgr.checkResourceLimit(vlanOwner, ResourceType.public_ip, accountIpRange);
}
}
// Check if the IP range overlaps with the private ip
if (ipv4) {
checkOverlapPrivateIpRange(zoneId, startIP, endIP);
}
return commitVlan(zoneId, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, forVirtualNetwork, forSystemVms, networkId, physicalNetworkId, startIPv6, endIPv6, ip6Gateway,
ip6Cidr, domain, vlanOwner, network, sameSubnet, cmd.getProvider());
long reservedIpAddressesAmount = 0L;
if (forVirtualNetwork && vlanOwner != null) {
reservedIpAddressesAmount = NetUtils.ip2Long(endIP) - NetUtils.ip2Long(startIP) + 1;
}
try (CheckedReservation publicIpReservation = new CheckedReservation(vlanOwner, ResourceType.public_ip, null, null, null, reservedIpAddressesAmount, null, reservationDao, _resourceLimitMgr)) {
return commitVlan(zoneId, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, forVirtualNetwork, forSystemVms, networkId, physicalNetworkId, startIPv6, endIPv6, ip6Gateway,
ip6Cidr, domain, vlanOwner, network, sameSubnet, cmd.getProvider());
}
}
private Network getNetwork(Long networkId) {
@ -5897,7 +5898,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
final VlanVO vlan = commitVlanAndIpRange(zoneId, networkId, physicalNetworkId, podId, startIP, endIP, vlanGateway, vlanNetmask, vlanId, domain, vlanOwner, vlanIp6Gateway, vlanIp6Cidr,
ipv4, zone, vlanType, ipv6Range, ipRange, forSystemVms, provider);
if (vlan != null) {
if (vlan != null && network.getTrafficType() != TrafficType.Public) {
if (ipv4) {
addCidrAndGatewayForIpv4(networkId, vlanGateway, vlanNetmask);
} else if (ipv6) {
@ -6063,7 +6064,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
ip.isSourceNat(), vlan.getVlanType().toString(), ip.getSystem(), usageHidden, ip.getClass().getName(), ip.getUuid());
}
// increment resource count for dedicated public ip's
_resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, new Long(ips.size()));
_resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, (long)ips.size());
} else if (domain != null && !forSystemVms) {
// This VLAN is domain-wide, so create a DomainVlanMapVO entry
final DomainVlanMapVO domainVlanMapVO = new DomainVlanMapVO(domain.getId(), vlan.getId());
@ -6107,7 +6108,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
String endIpv6,
String ip6Gateway,
String ip6Cidr,
Boolean forSystemVms) throws ConcurrentOperationException {
Boolean forSystemVms) throws ConcurrentOperationException, ResourceAllocationException {
VlanVO vlanRange = _vlanDao.findById(id);
if (vlanRange == null) {
@ -6127,24 +6128,49 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
AccountVlanMapVO accountMap = _accountVlanMapDao.findAccountVlanMap(null, id);
Account account = accountMap != null ? _accountDao.findById(accountMap.getAccountId()) : null;
DomainVlanMapVO domainMap = _domainVlanMapDao.findDomainVlanMap(null, id);
Long domainId = domainMap != null ? domainMap.getDomainId() : null;
final Boolean isRangeForSystemVM = checkIfVlanRangeIsForSystemVM(id);
if (forSystemVms != null && isRangeForSystemVM != forSystemVms) {
if (VlanType.DirectAttached.equals(vlanRange.getVlanType())) {
throw new InvalidParameterValueException("forSystemVms is not available for this IP range with vlan type: " + VlanType.DirectAttached);
}
// Check if range has already been dedicated
final List<AccountVlanMapVO> maps = _accountVlanMapDao.listAccountVlanMapsByVlan(id);
if (maps != null && !maps.isEmpty()) {
if (account != null) {
throw new InvalidParameterValueException("Specified Public IP range has already been dedicated to an account");
}
List<DomainVlanMapVO> domainmaps = _domainVlanMapDao.listDomainVlanMapsByVlan(id);
if (domainmaps != null && !domainmaps.isEmpty()) {
if (domainId != null) {
throw new InvalidParameterValueException("Specified Public IP range has already been dedicated to a domain");
}
}
if (ipv4) {
updateVlanAndIpv4Range(id, vlanRange, startIp, endIp, gateway, netmask, isRangeForSystemVM, forSystemVms);
long existingIpAddressAmount = 0L;
long newIpAddressAmount = 0L;
if (account != null) {
// IPv4 public range is dedicated to an account (IPv6 cannot be dedicated at the moment).
// We need to update the resource count.
existingIpAddressAmount = _publicIpAddressDao.countIPs(vlanRange.getDataCenterId(), id, false);
newIpAddressAmount = NetUtils.ip2Long(endIp) - NetUtils.ip2Long(startIp) + 1;
}
try (CheckedReservation publicIpReservation = new CheckedReservation(account, ResourceType.public_ip, null, null, null, newIpAddressAmount, existingIpAddressAmount, reservationDao, _resourceLimitMgr)) {
updateVlanAndIpv4Range(id, vlanRange, startIp, endIp, gateway, netmask, isRangeForSystemVM, forSystemVms);
if (account != null) {
long countDiff = newIpAddressAmount - existingIpAddressAmount;
if (countDiff > 0) {
_resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.public_ip, countDiff);
} else if (countDiff < 0) {
_resourceLimitMgr.decrementResourceCount(account.getId(), ResourceType.public_ip, Math.abs(countDiff));
}
}
}
}
if (ipv6) {
updateVlanAndIpv6Range(id, vlanRange, startIpv6, endIpv6, ip6Gateway, ip6Cidr, isRangeForSystemVM, forSystemVms);
@ -6423,7 +6449,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
} finally {
_vlanDao.releaseFromLockTable(vlanDbId);
if (resourceCountToBeDecrement > 0) { //Making sure to decrement the count of only success operations above. For any reaason if disassociation fails then this number will vary from original range length.
_resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), ResourceType.public_ip, new Long(resourceCountToBeDecrement));
_resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), ResourceType.public_ip, (long)resourceCountToBeDecrement);
}
}
} else { // !isAccountSpecific
@ -6531,12 +6557,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
throw new InvalidParameterValueException("Public IP range can be dedicated to an account only in the zone of type " + NetworkType.Advanced);
}
// Check Public IP resource limits
if (vlanOwner != null) {
final int accountPublicIpRange = _publicIpAddressDao.countIPs(zoneId, vlanDbId, false);
_resourceLimitMgr.checkResourceLimit(vlanOwner, ResourceType.public_ip, accountPublicIpRange);
}
// Check if any of the Public IP addresses is allocated to another
// account
final List<IPAddressVO> ips = _publicIpAddressDao.listByVlanId(vlanDbId);
@ -6557,29 +6577,35 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
if (vlanOwner != null) {
// Create an AccountVlanMapVO entry
final AccountVlanMapVO accountVlanMapVO = new AccountVlanMapVO(vlanOwner.getId(), vlan.getId());
_accountVlanMapDao.persist(accountVlanMapVO);
// Check Public IP resource limits
long reservedIpAddressesAmount = vlanOwner != null ? _publicIpAddressDao.countIPs(zoneId, vlanDbId, false) : 0L;
try (CheckedReservation publicIpReservation = new CheckedReservation(vlanOwner, ResourceType.public_ip, null, null, null, reservedIpAddressesAmount, null, reservationDao, _resourceLimitMgr)) {
// generate usage event for dedication of every ip address in the range
for (final IPAddressVO ip : ips) {
final boolean usageHidden = _ipAddrMgr.isUsageHidden(ip);
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, vlanOwner.getId(), ip.getDataCenterId(), ip.getId(), ip.getAddress().toString(), ip.isSourceNat(),
vlan.getVlanType().toString(), ip.getSystem(), usageHidden, ip.getClass().getName(), ip.getUuid());
if (vlanOwner != null) {
// Create an AccountVlanMapVO entry
final AccountVlanMapVO accountVlanMapVO = new AccountVlanMapVO(vlanOwner.getId(), vlan.getId());
_accountVlanMapDao.persist(accountVlanMapVO);
// generate usage event for dedication of every ip address in the range
for (final IPAddressVO ip : ips) {
final boolean usageHidden = _ipAddrMgr.isUsageHidden(ip);
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, vlanOwner.getId(), ip.getDataCenterId(), ip.getId(), ip.getAddress().toString(), ip.isSourceNat(),
vlan.getVlanType().toString(), ip.getSystem(), usageHidden, ip.getClass().getName(), ip.getUuid());
}
} else if (domain != null) {
// Create an DomainVlanMapVO entry
DomainVlanMapVO domainVlanMapVO = new DomainVlanMapVO(domain.getId(), vlan.getId());
_domainVlanMapDao.persist(domainVlanMapVO);
}
} else if (domain != null) {
// Create an DomainVlanMapVO entry
DomainVlanMapVO domainVlanMapVO = new DomainVlanMapVO(domain.getId(), vlan.getId());
_domainVlanMapDao.persist(domainVlanMapVO);
}
// increment resource count for dedicated public ip's
if (vlanOwner != null) {
_resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, new Long(ips.size()));
}
// increment resource count for dedicated public ip's
if (vlanOwner != null) {
_resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, (long)ips.size());
}
return vlan;
return vlan;
}
}
@Override
@ -6668,7 +6694,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
// decrement resource count for dedicated public ip's
_resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), ResourceType.public_ip, new Long(ips.size()));
_resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), ResourceType.public_ip, (long)ips.size());
success = true;
} else if (isDomainSpecific && _domainVlanMapDao.remove(domainVlan.get(0).getId())) {
logger.debug("Remove the vlan from domain_vlan_map successfully.");
@ -6837,7 +6863,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
final List<Object> newCidrPair = new ArrayList<>();
newCidrPair.add(0, getCidrAddress(cidr));
newCidrPair.add(1, (long)getCidrSize(cidr));
currentPodCidrSubnets.put(new Long(-1), newCidrPair);
currentPodCidrSubnets.put(-1L, newCidrPair);
final DataCenterVO dcVo = _zoneDao.findById(dcId);
final String guestNetworkCidr = dcVo.getGuestNetworkCidr();
@ -6937,7 +6963,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
private String getPodName(final long podId) {
return _podDao.findById(new Long(podId)).getName();
return _podDao.findById(podId).getName();
}
private boolean validZone(final String zoneName) {
@ -6949,7 +6975,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
private String getZoneName(final long zoneId) {
final DataCenterVO zone = _zoneDao.findById(new Long(zoneId));
final DataCenterVO zone = _zoneDao.findById(zoneId);
if (zone != null) {
return zone.getName();
} else {
@ -6976,11 +7002,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
final boolean ipv4 = deletedVlan.getVlanGateway() != null;
final boolean ipv6 = deletedVlan.getIp6Gateway() != null;
final long networkId = deletedVlan.getNetworkId();
final NetworkVO networkVO = _networkDao.findById(networkId);
if (ipv4) {
removeCidrAndGatewayForIpv4(networkId, deletedVlan);
} else if (ipv6) {
removeCidrAndGatewayForIpv6(networkId, deletedVlan);
if (networkVO != null && networkVO.getTrafficType() != TrafficType.Public) {
if (ipv4) {
removeCidrAndGatewayForIpv4(networkId, deletedVlan);
} else if (ipv6) {
removeCidrAndGatewayForIpv6(networkId, deletedVlan);
}
}
messageBus.publish(_name, MESSAGE_DELETE_VLAN_IP_RANGE_EVENT, PublishScope.LOCAL, deletedVlan);

View File

@ -69,9 +69,6 @@ public interface ConsoleProxyManager extends Manager, ConsoleProxyService {
ConfigKey<String> ConsoleProxyCapacityScanInterval = new ConfigKey<>(String.class, "consoleproxy.capacityscan.interval", "Console Proxy", "30000",
"The time interval(in millisecond) to scan whether or not system needs more console proxy to ensure minimal standby capacity", false, null);
ConfigKey<Integer> ConsoleProxyCmdPort = new ConfigKey<>(Integer.class, "consoleproxy.cmd.port", "Console Proxy", String.valueOf(DEFAULT_PROXY_CMD_PORT),
"Console proxy command port that is used to communicate with management server", false, ConfigKey.Scope.Zone, null);
ConfigKey<Boolean> ConsoleProxyRestart = new ConfigKey<>(Boolean.class, "consoleproxy.restart", "Console Proxy", "true",
"Console proxy restart flag, defaults to true", true, ConfigKey.Scope.Zone, null);

View File

@ -1589,7 +1589,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {ConsoleProxySslEnabled, NoVncConsoleDefault, NoVncConsoleSourceIpCheckEnabled, ConsoleProxyServiceOffering,
ConsoleProxyCapacityStandby, ConsoleProxyCapacityScanInterval, ConsoleProxyCmdPort, ConsoleProxyRestart, ConsoleProxyUrlDomain, ConsoleProxySessionMax, ConsoleProxySessionTimeout, ConsoleProxyDisableRpFilter, ConsoleProxyLaunchMax,
ConsoleProxyCapacityStandby, ConsoleProxyCapacityScanInterval, ConsoleProxyRestart, ConsoleProxyUrlDomain, ConsoleProxySessionMax, ConsoleProxySessionTimeout, ConsoleProxyDisableRpFilter, ConsoleProxyLaunchMax,
ConsoleProxyManagementLastState, ConsoleProxyServiceManagementState, NoVncConsoleShowDot,
ConsoleProxyVmUserData};
}

View File

@ -41,6 +41,7 @@ import java.util.UUID;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.resourcelimit.CheckedReservation;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.alert.AlertService;
@ -76,6 +77,7 @@ import org.apache.cloudstack.network.NetworkPermissionVO;
import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.apache.cloudstack.network.dao.NetworkPermissionDao;
import org.apache.cloudstack.network.element.InternalLoadBalancerElementService;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
@ -338,6 +340,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
@Inject
ResourceLimitService _resourceLimitMgr;
@Inject
ReservationDao reservationDao;
@Inject
DomainManager _domainMgr;
@Inject
ProjectManager _projectMgr;
@ -1155,15 +1159,10 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
if (ipDedicatedAccountId != null && !ipDedicatedAccountId.equals(account.getAccountId())) {
throw new InvalidParameterValueException("Unable to reserve a IP because it is dedicated to another Account.");
}
if (ipDedicatedAccountId == null) {
// Check that the maximum number of public IPs for the given accountId will not be exceeded
try {
_resourceLimitMgr.checkResourceLimit(account, Resource.ResourceType.public_ip);
} catch (ResourceAllocationException ex) {
logger.warn("Failed to allocate resource of type " + ex.getResourceType() + " for account " + account);
throw new AccountLimitException("Maximum number of public IP addresses for account: " + account.getAccountName() + " has been exceeded.");
}
}
long reservedIpAddressesAmount = ipDedicatedAccountId == null ? 1L : 0L;
try (CheckedReservation publicIpAddressReservation = new CheckedReservation(account, Resource.ResourceType.public_ip, reservedIpAddressesAmount, reservationDao, _resourceLimitMgr)) {
List<AccountVlanMapVO> maps = _accountVlanMapDao.listAccountVlanMapsByVlan(ipVO.getVlanId());
ipVO.setAllocatedTime(new Date());
ipVO.setAllocatedToAccountId(account.getAccountId());
@ -1173,10 +1172,15 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
ipVO.setDisplay(displayIp);
}
ipVO = _ipAddressDao.persist(ipVO);
if (ipDedicatedAccountId == null) {
if (reservedIpAddressesAmount > 0) {
_resourceLimitMgr.incrementResourceCount(account.getId(), Resource.ResourceType.public_ip);
}
return ipVO;
} catch (ResourceAllocationException ex) {
logger.warn("Failed to allocate resource of type " + ex.getResourceType() + " for account " + account);
throw new AccountLimitException("Maximum number of public IP addresses for account: " + account.getAccountName() + " has been exceeded.");
}
}
@Override
@ -3216,7 +3220,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
if (displayNetwork != null && displayNetwork != network.getDisplayNetwork()) {
// Update resource count if it needs to be updated
NetworkOffering networkOffering = _networkOfferingDao.findById(network.getNetworkOfferingId());
if (_networkMgr.resourceCountNeedsUpdate(networkOffering, network.getAclType())) {
if (_networkMgr.isResourceCountUpdateNeeded(networkOffering)) {
_resourceLimitMgr.changeResourceCount(network.getAccountId(), Resource.ResourceType.network, displayNetwork);
}

View File

@ -42,30 +42,8 @@ import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.configuration.ConfigurationManagerImpl;
import com.cloud.bgp.BGPService;
import com.cloud.dc.ASNumberVO;
import com.cloud.dc.dao.ASNumberDao;
import com.cloud.dc.Vlan;
import com.cloud.network.RemoteAccessVpn;
import com.cloud.network.Site2SiteVpnConnection;
import com.cloud.network.dao.NetrisProviderDao;
import com.cloud.network.dao.NsxProviderDao;
import com.cloud.network.dao.RemoteAccessVpnDao;
import com.cloud.network.dao.RemoteAccessVpnVO;
import com.cloud.network.dao.Site2SiteCustomerGatewayDao;
import com.cloud.network.dao.Site2SiteCustomerGatewayVO;
import com.cloud.network.dao.Site2SiteVpnConnectionDao;
import com.cloud.network.dao.Site2SiteVpnConnectionVO;
import com.cloud.network.element.NetrisProviderVO;
import com.cloud.network.element.NetworkACLServiceProvider;
import com.cloud.network.element.NsxProviderVO;
import com.cloud.network.rules.RulesManager;
import com.cloud.network.vpn.RemoteAccessVpnService;
import com.cloud.utils.DomainHelper;
import com.cloud.vm.dao.VMInstanceDao;
import com.google.common.collect.Sets;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.annotation.AnnotationService;
@ -108,12 +86,18 @@ import com.cloud.agent.manager.Commands;
import com.cloud.alert.AlertManager;
import com.cloud.api.query.dao.VpcOfferingJoinDao;
import com.cloud.api.query.vo.VpcOfferingJoinVO;
import com.cloud.bgp.BGPService;
import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.configuration.ConfigurationManagerImpl;
import com.cloud.configuration.Resource.ResourceType;
import com.cloud.dc.ASNumberVO;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.Vlan.VlanType;
import com.cloud.dc.Vlan;
import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.ASNumberDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.VlanDao;
import com.cloud.deploy.DeployDestination;
@ -143,18 +127,33 @@ import com.cloud.network.NetworkService;
import com.cloud.network.Networks.BroadcastDomainType;
import com.cloud.network.Networks.TrafficType;
import com.cloud.network.PhysicalNetwork;
import com.cloud.network.RemoteAccessVpn;
import com.cloud.network.Site2SiteVpnConnection;
import com.cloud.network.addr.PublicIp;
import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.dao.NetrisProviderDao;
import com.cloud.network.dao.NsxProviderDao;
import com.cloud.network.dao.RemoteAccessVpnDao;
import com.cloud.network.dao.RemoteAccessVpnVO;
import com.cloud.network.dao.Site2SiteCustomerGatewayDao;
import com.cloud.network.dao.Site2SiteCustomerGatewayVO;
import com.cloud.network.dao.Site2SiteVpnConnectionDao;
import com.cloud.network.dao.Site2SiteVpnConnectionVO;
import com.cloud.network.element.NetrisProviderVO;
import com.cloud.network.element.NetworkACLServiceProvider;
import com.cloud.network.element.NetworkElement;
import com.cloud.network.element.NsxProviderVO;
import com.cloud.network.element.StaticNatServiceProvider;
import com.cloud.network.element.VpcProvider;
import com.cloud.network.router.CommandSetupHelper;
import com.cloud.network.router.NetworkHelper;
import com.cloud.network.router.VpcVirtualNetworkApplianceManager;
import com.cloud.network.rules.RulesManager;
import com.cloud.network.vpn.RemoteAccessVpnService;
import com.cloud.network.vpc.VpcOffering.State;
import com.cloud.network.vpc.dao.NetworkACLDao;
import com.cloud.network.vpc.dao.PrivateIpDao;
@ -173,6 +172,7 @@ import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
import com.cloud.org.Grouping;
import com.cloud.projects.Project.ListProjectResourcesCriteria;
import com.cloud.resourcelimit.CheckedReservation;
import com.cloud.server.ResourceTag.ResourceObjectType;
import com.cloud.tags.ResourceTagVO;
import com.cloud.tags.dao.ResourceTagDao;
@ -180,6 +180,7 @@ import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.ResourceLimitService;
import com.cloud.user.User;
import com.cloud.utils.DomainHelper;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.StringUtils;
@ -209,6 +210,7 @@ import com.cloud.vm.ReservationContextImpl;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.VMInstanceDao;
import static com.cloud.offering.NetworkOffering.RoutingMode.Dynamic;
@ -1573,9 +1575,6 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
// Verify that caller can perform actions in behalf of vpc owner
_accountMgr.checkAccess(caller, null, false, owner);
// check resource limit
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.vpc);
// Validate zone
final DataCenter zone = _dcDao.findById(zoneId);
if (zone == null) {
@ -1670,6 +1669,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
vpc.setDisplay(Boolean.TRUE.equals(displayVpc));
vpc.setUseRouterIpResolver(Boolean.TRUE.equals(useVrIpResolver));
try (CheckedReservation vpcReservation = new CheckedReservation(owner, ResourceType.vpc, null, null, 1L, reservationDao, _resourceLimitMgr)) {
if (vpc.getCidr() == null && cidrSize != null) {
// Allocate a CIDR for VPC
Ipv4GuestSubnetNetworkMap subnet = routedIpv4Manager.getOrCreateIpv4SubnetForVpc(vpc, cidrSize);
@ -1689,6 +1689,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
routedIpv4Manager.persistBgpPeersForVpc(newVpc.getId(), bgpPeerIds);
}
return newVpc;
}
}
private void validateVpcCidrSize(Account caller, long accountId, VpcOffering vpcOffering, String cidr, Integer cidrSize, long zoneId) {

View File

@ -36,6 +36,7 @@ import javax.inject.Inject;
import javax.mail.MessagingException;
import javax.naming.ConfigurationException;
import com.cloud.resourcelimit.CheckedReservation;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.ProjectRole;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
@ -47,6 +48,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.messagebus.MessageBus;
import org.apache.cloudstack.framework.messagebus.PublishScope;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.cloudstack.utils.mailing.MailAddress;
import org.apache.cloudstack.utils.mailing.SMTPMailProperties;
import org.apache.cloudstack.utils.mailing.SMTPMailSender;
@ -159,6 +161,8 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
private VpcManager _vpcMgr;
@Inject
MessageBus messageBus;
@Inject
private ReservationDao reservationDao;
protected boolean _invitationRequired = false;
protected long _invitationTimeOut = 86400000;
@ -272,8 +276,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
owner = _accountDao.findById(user.getAccountId());
}
//do resource limit check
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.project);
try (CheckedReservation projectReservation = new CheckedReservation(owner, ResourceType.project, null, null, 1L, reservationDao, _resourceLimitMgr)) {
final Account ownerFinal = owner;
User finalUser = user;
@ -308,6 +311,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
messageBus.publish(_name, ProjectManager.MESSAGE_CREATE_TUNGSTEN_PROJECT_EVENT, PublishScope.LOCAL, project);
return project;
}
}
@Override
@ -491,6 +495,9 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
//remove account
ProjectAccountVO projectAccount = _projectAccountDao.findByProjectIdAccountId(projectId, account.getId());
success = _projectAccountDao.remove(projectAccount.getId());
if (projectAccount.getAccountRole() == Role.Admin) {
_resourceLimitMgr.decrementResourceCount(account.getId(), ResourceType.project);
}
//remove all invitations for account
if (success) {
@ -537,7 +544,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
@Override
@ActionEvent(eventType = EventTypes.EVENT_PROJECT_USER_ADD, eventDescription = "adding user to project", async = true)
public boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) {
public boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) throws ResourceAllocationException {
Account caller = CallContext.current().getCallingAccount();
Project project = getProject(projectId);
@ -594,12 +601,20 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
if (username == null) {
throw new InvalidParameterValueException("User information (ID) is required to add user to the project");
}
boolean shouldIncrementResourceCount = projectRole != null && Role.Admin == projectRole;
try (CheckedReservation cr = new CheckedReservation(userAccount, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) {
if (assignUserToProject(project, user.getId(), user.getAccountId(), projectRole,
Optional.ofNullable(role).map(ProjectRole::getId).orElse(null)) != null) {
if (shouldIncrementResourceCount) {
_resourceLimitMgr.incrementResourceCount(userAccount.getId(), ResourceType.project);
}
return true;
} else {
logger.warn("Failed to add user to project: {}", project);
return false;
}
}
logger.warn("Failed to add user to project: {}", project);
return false;
}
}
@ -652,14 +667,18 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
}
private void updateProjectAccount(ProjectAccountVO futureOwner, Role newAccRole, Long accountId) throws ResourceAllocationException {
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(accountId), ResourceType.project);
Account account = _accountMgr.getAccount(accountId);
boolean shouldIncrementResourceCount = Role.Admin == newAccRole;
try (CheckedReservation checkedReservation = new CheckedReservation(account, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) {
futureOwner.setAccountRole(newAccRole);
_projectAccountDao.update(futureOwner.getId(), futureOwner);
if (newAccRole != null && Role.Admin == newAccRole) {
if (shouldIncrementResourceCount) {
_resourceLimitMgr.incrementResourceCount(accountId, ResourceType.project);
} else {
_resourceLimitMgr.decrementResourceCount(accountId, ResourceType.project);
}
}
}
@Override
@ -701,9 +720,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
" doesn't belong to the project. Add it to the project first and then change the project's ownership");
}
//do resource limit check
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(futureOwnerAccount.getId()), ResourceType.project);
try (CheckedReservation checkedReservation = new CheckedReservation(futureOwnerAccount, ResourceType.project, null, null, 1L, reservationDao, _resourceLimitMgr)) {
//unset the role for the old owner
ProjectAccountVO currentOwner = _projectAccountDao.findByProjectIdAccountId(projectId, currentOwnerAccount.getId());
currentOwner.setAccountRole(Role.Regular);
@ -714,7 +731,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
futureOwner.setAccountRole(Role.Admin);
_projectAccountDao.update(futureOwner.getId(), futureOwner);
_resourceLimitMgr.incrementResourceCount(futureOwnerAccount.getId(), ResourceType.project);
}
} else {
logger.trace("Future owner {}is already the owner of the project {}", newOwnerName, project);
}
@ -792,7 +809,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
@Override
@ActionEvent(eventType = EventTypes.EVENT_PROJECT_ACCOUNT_ADD, eventDescription = "adding account to project", async = true)
public boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType) {
public boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType) throws ResourceAllocationException {
Account caller = CallContext.current().getCallingAccount();
//check that the project exists
@ -857,13 +874,20 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
if (account == null) {
throw new InvalidParameterValueException("Account information is required for assigning account to the project");
}
boolean shouldIncrementResourceCount = projectRoleType != null && Role.Admin == projectRoleType;
try (CheckedReservation cr = new CheckedReservation(account, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) {
if (assignAccountToProject(project, account.getId(), projectRoleType, null,
Optional.ofNullable(projectRole).map(ProjectRole::getId).orElse(null)) != null) {
if (shouldIncrementResourceCount) {
_resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.project);
}
return true;
} else {
logger.warn("Failed to add account {} to project {}", accountName, project);
return false;
}
}
}
}
@ -1042,7 +1066,9 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
boolean success = true;
ProjectAccountVO projectAccount = _projectAccountDao.findByProjectIdUserId(projectId, user.getAccountId(), user.getId());
success = _projectAccountDao.remove(projectAccount.getId());
if (projectAccount.getAccountRole() == Role.Admin) {
_resourceLimitMgr.decrementResourceCount(user.getAccountId(), ResourceType.project);
}
if (success) {
logger.debug("Removed user {} from project. Removing any invite sent to the user", user);
ProjectInvitation invite = _projectInvitationDao.findByUserIdProjectId(user.getId(), user.getAccountId(), projectId);

View File

@ -20,13 +20,17 @@ package com.cloud.resourcelimit;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import com.cloud.api.ApiDBUtils;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.reservation.ReservationVO;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.cloudstack.resourcelimit.Reserver;
import org.apache.cloudstack.user.ResourceReservation;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -40,7 +44,7 @@ import com.cloud.utils.db.GlobalLock;
import com.cloud.utils.exception.CloudRuntimeException;
public class CheckedReservation implements AutoCloseable {
public class CheckedReservation implements Reserver {
protected Logger logger = LogManager.getLogger(getClass());
private static final int TRY_TO_GET_LOCK_TIME = 120;
@ -48,11 +52,15 @@ public class CheckedReservation implements AutoCloseable {
ReservationDao reservationDao;
ResourceLimitService resourceLimitService;
private final Account account;
private final ResourceType resourceType;
private Long amount;
private Account account;
private Long domainId;
private ResourceType resourceType;
private Long resourceId;
private Long reservationAmount;
private List<String> reservationTags;
private Long existingAmount;
private List<String> existingLimitTags;
private List<ResourceReservation> reservations;
private List<String> resourceLimitTags;
private String getContextParameterKey() {
return getResourceReservationContextParameterKey(resourceType);
@ -73,12 +81,12 @@ public class CheckedReservation implements AutoCloseable {
this.reservations = null;
}
protected void checkLimitAndPersistReservations(Account account, ResourceType resourceType, Long resourceId, List<String> resourceLimitTags, Long amount) throws ResourceAllocationException {
protected void checkLimitAndPersistReservations(Account account, Long domainId, ResourceType resourceType, Long resourceId, List<String> resourceLimitTags, Long amount) throws ResourceAllocationException {
try {
checkLimitAndPersistReservation(account, resourceType, resourceId, null, amount);
checkLimitAndPersistReservation(account, domainId, resourceType, resourceId, null, amount);
if (CollectionUtils.isNotEmpty(resourceLimitTags)) {
for (String tag : resourceLimitTags) {
checkLimitAndPersistReservation(account, resourceType, resourceId, tag, amount);
checkLimitAndPersistReservation(account, domainId, resourceType, resourceId, tag, amount);
}
}
} catch (ResourceAllocationException rae) {
@ -87,11 +95,11 @@ public class CheckedReservation implements AutoCloseable {
}
}
protected void checkLimitAndPersistReservation(Account account, ResourceType resourceType, Long resourceId, String tag, Long amount) throws ResourceAllocationException {
protected void checkLimitAndPersistReservation(Account account, Long domainId, ResourceType resourceType, Long resourceId, String tag, Long amount) throws ResourceAllocationException {
if (amount > 0) {
resourceLimitService.checkResourceLimitWithTag(account, resourceType, tag, amount);
resourceLimitService.checkResourceLimitWithTag(account, domainId, true, resourceType, tag, amount);
}
ReservationVO reservationVO = new ReservationVO(account.getAccountId(), account.getDomainId(), resourceType, tag, amount);
ReservationVO reservationVO = new ReservationVO(account.getAccountId(), domainId, resourceType, tag, amount);
if (resourceId != null) {
reservationVO.setResourceId(resourceId);
}
@ -99,9 +107,25 @@ public class CheckedReservation implements AutoCloseable {
this.reservations.add(reservation);
}
public CheckedReservation(Account account, ResourceType resourceType, List<String> resourceLimitTags, Long amount,
ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException {
this(account, resourceType, null, resourceLimitTags, amount, reservationDao, resourceLimitService);
// TODO: refactor these into a Builder to avoid having so many constructors
public CheckedReservation(Account account, ResourceType resourceType, List<String> resourceLimitTags, Long reservationAmount,
ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException {
this(account, resourceType, null, resourceLimitTags, null, reservationAmount, null, reservationDao, resourceLimitService);
}
public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List<String> reservedTags,
List<String> existingTags, Long reservationAmount, Long existingAmount, ReservationDao reservationDao,
ResourceLimitService resourceLimitService) throws ResourceAllocationException {
this(account, null, resourceType, resourceId, reservedTags, existingTags, reservationAmount, existingAmount, reservationDao, resourceLimitService);
}
public CheckedReservation(Account account, Long domainId, ResourceType resourceType, Long resourceId, List<String> reservedTags,
Long reservationAmount, ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException {
this(account, domainId, resourceType, resourceId, reservedTags, null, reservationAmount, null, reservationDao, resourceLimitService);
}
public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List<String> reservedTags, Long reservationAmount, ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException {
this(account, null, resourceType, resourceId, reservedTags, null, reservationAmount, null, reservationDao, resourceLimitService);
}
/**
@ -109,25 +133,48 @@ public class CheckedReservation implements AutoCloseable {
* - create DB entry for reservation
* - hold the id of this record as a ticket for implementation
*
* @param amount positive number of the resource type to reserve
* @param reservationAmount positive number of the resource type to reserve
* @throws ResourceAllocationException
*/
public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List<String> resourceLimitTags, Long amount,
ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException {
public CheckedReservation(Account account, Long domainId, ResourceType resourceType, Long resourceId, List<String> reservedTags,
List<String> existingTags, Long reservationAmount, Long existingAmount, ReservationDao reservationDao,
ResourceLimitService resourceLimitService) throws ResourceAllocationException {
if (ObjectUtils.allNull(account, domainId)) {
logger.debug("Not reserving any {} resources, as no account/domain was provided.", resourceType);
return;
}
this.reservationDao = reservationDao;
this.resourceLimitService = resourceLimitService;
this.account = account;
this.resourceType = resourceType;
this.amount = amount;
this.reservations = new ArrayList<>();
this.resourceLimitTags = resourceLimitTags;
if (this.amount != null && this.amount != 0) {
if (amount > 0) {
// When allocating to a domain instead of a specific account, consider the system account as the owner for the validations here.
if (account == null) {
account = ApiDBUtils.getSystemAccount();
}
this.account = account;
if (domainId == null) {
domainId = account.getDomainId();
}
this.domainId = domainId;
this.resourceType = resourceType;
this.reservationAmount = reservationAmount;
this.existingAmount = existingAmount;
this.reservations = new ArrayList<>();
this.reservationTags = getTagsWithoutNull(reservedTags);
this.existingLimitTags = getTagsWithoutNull(existingTags);
// TODO: refactor me
if (this.reservationAmount != null && this.reservationAmount != 0) {
if (reservationAmount > 0) {
setGlobalLock();
if (quotaLimitLock.lock(TRY_TO_GET_LOCK_TIME)) {
try {
checkLimitAndPersistReservations(account, resourceType, resourceId, resourceLimitTags, amount);
adjustCountToNotConsiderExistingAmount();
checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, reservationTags, reservationAmount);
CallContext.current().putContextParameter(getContextParameterKey(), getIds());
} catch (NullPointerException npe) {
throw new CloudRuntimeException("not enough means to check limits", npe);
@ -138,14 +185,28 @@ public class CheckedReservation implements AutoCloseable {
throw new ResourceAllocationException(String.format("unable to acquire resource reservation \"%s\"", quotaLimitLock.getName()), resourceType);
}
} else {
checkLimitAndPersistReservations(account, resourceType, resourceId, resourceLimitTags, amount);
checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, reservationTags, reservationAmount);
}
} else {
logger.debug("not reserving any amount of resources for {} in domain {}, type: {}, tag: {}",
account.getAccountName(), account.getDomainId(), resourceType, getResourceLimitTagsAsString());
account.getAccountName(), this.domainId, resourceType, getResourceLimitTagsAsString());
}
}
protected List<String> getTagsWithoutNull(List<String> tags) {
if (tags == null) {
return null;
}
return tags.stream().filter(Objects::nonNull).collect(Collectors.toList());
}
protected void adjustCountToNotConsiderExistingAmount() throws ResourceAllocationException {
if (existingAmount == null || existingAmount == 0) {
return;
}
checkLimitAndPersistReservations(account, domainId, resourceType, resourceId, existingLimitTags, -1 * existingAmount);
}
public CheckedReservation(Account account, ResourceType resourceType, Long amount, ReservationDao reservationDao,
ResourceLimitService resourceLimitService) throws ResourceAllocationException {
this(account, resourceType, null, amount, reservationDao, resourceLimitService);
@ -153,7 +214,7 @@ public class CheckedReservation implements AutoCloseable {
@NotNull
private void setGlobalLock() {
String lockName = String.format("CheckedReservation-%s/%d", account.getDomainId(), resourceType.getOrdinal());
String lockName = String.format("CheckedReservation-%s/%d", this.domainId, resourceType.getOrdinal());
setQuotaLimitLock(GlobalLock.getInternLock(lockName));
}
@ -162,7 +223,7 @@ public class CheckedReservation implements AutoCloseable {
}
@Override
public void close() throws Exception {
public void close() {
removeAllReservations();
}
@ -171,11 +232,11 @@ public class CheckedReservation implements AutoCloseable {
}
public String getResourceLimitTagsAsString() {
return CollectionUtils.isNotEmpty(resourceLimitTags) ? StringUtils.join(resourceLimitTags) : null;
return CollectionUtils.isNotEmpty(reservationTags) ? StringUtils.join(reservationTags) : null;
}
public Long getReservedAmount() {
return amount;
return reservationAmount;
}
public List<ResourceReservation> getReservations() {

View File

@ -0,0 +1,35 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
package com.cloud.resourcelimit;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.resourcelimit.Reserver;
import java.util.List;
public class ReservationHelper {
public static void closeAll(List<Reserver> reservations) throws CloudRuntimeException {
for (Reserver reservation : reservations) {
reservation.close();
}
}
}

View File

@ -54,6 +54,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.reservation.ReservationVO;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.cloudstack.resourcelimit.Reserver;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
@ -92,6 +93,7 @@ import com.cloud.exception.ResourceAllocationException;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkDomainDao;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.ServiceOffering;
@ -175,6 +177,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
@Inject
private ReservationDao reservationDao;
@Inject
private ResourceLimitService resourceLimitService;
@Inject
protected SnapshotDao _snapshotDao;
@Inject
protected BackupDao backupDao;
@ -204,6 +208,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
DiskOfferingDao diskOfferingDao;
@Inject
BucketDao bucketDao;
@Inject
private NetworkDomainDao networkDomainDao;
protected GenericSearchBuilder<TemplateDataStoreVO, SumCount> templateSizeSearch;
protected GenericSearchBuilder<SnapshotDataStoreVO, SumCount> snapshotSizeSearch;
@ -267,7 +273,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
templateSizeSearch = _vmTemplateStoreDao.createSearchBuilder(SumCount.class);
templateSizeSearch.select("sum", Func.SUM, templateSizeSearch.entity().getSize());
templateSizeSearch.and("downloadState", templateSizeSearch.entity().getDownloadState(), Op.EQ);
templateSizeSearch.and("downloadState", templateSizeSearch.entity().getDownloadState(), Op.IN);
templateSizeSearch.and("destroyed", templateSizeSearch.entity().getDestroyed(), Op.EQ);
SearchBuilder<VMTemplateVO> join1 = _vmTemplateDao.createSearchBuilder();
join1.and("accountId", join1.entity().getAccountId(), Op.EQ);
@ -515,15 +521,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
return max;
}
protected void checkDomainResourceLimit(final Account account, final Project project, final ResourceType type, String tag, long numResources) throws ResourceAllocationException {
// check all domains in the account's domain hierarchy
Long domainId;
if (project != null) {
domainId = project.getDomainId();
} else {
domainId = account.getDomainId();
}
protected void checkDomainResourceLimit(Long domainId, final ResourceType type, String tag, long numResources) throws ResourceAllocationException {
while (domainId != null) {
DomainVO domain = _domainDao.findById(domainId);
// no limit check if it is ROOT domain
@ -645,11 +643,16 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
@Override
public void checkResourceLimitWithTag(final Account account, final ResourceType type, String tag, long... count) throws ResourceAllocationException {
checkResourceLimitWithTag(account, null, false, type, tag, count);
}
@Override
public void checkResourceLimitWithTag(final Account account, Long domainId, boolean considerSystemAccount, final ResourceType type, String tag, long... count) throws ResourceAllocationException {
final long numResources = ((count.length == 0) ? 1 : count[0]);
Project project = null;
// Don't place any limits on system or root admin accounts
if (_accountMgr.isRootAdmin(account.getId())) {
if (_accountMgr.isRootAdmin(account.getId()) && !(considerSystemAccount && Account.ACCOUNT_ID_SYSTEM == account.getId())) {
return;
}
@ -657,6 +660,14 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
project = _projectDao.findByProjectAccountId(account.getId());
}
if (domainId == null) {
if (project != null) {
domainId = project.getDomainId();
} else {
domainId = account.getDomainId();
}
}
Long domainIdFinal = domainId;
final Project projectFinal = project;
Transaction.execute(new TransactionCallbackWithExceptionNoReturn<ResourceAllocationException>() {
@Override
@ -666,7 +677,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
// Check account limits
checkAccountResourceLimit(account, projectFinal, type, tag, numResources);
// check all domains in the account's domain hierarchy
checkDomainResourceLimit(account, projectFinal, type, tag, numResources);
checkDomainResourceLimit(domainIdFinal, type, tag, numResources);
}
});
}
@ -1208,7 +1219,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
* @param type the resource type to do the recalculation for
* @return the resulting new resource count
*/
protected long recalculateDomainResourceCount(final long domainId, final ResourceType type, String tag) {
public long recalculateDomainResourceCount(final long domainId, final ResourceType type, String tag) {
List<AccountVO> accounts = _accountDao.findActiveAccountsForDomain(domainId);
List<DomainVO> childDomains = _domainDao.findImmediateChildrenForParent(domainId);
@ -1244,9 +1255,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
long newResourceCount = 0L;
ResourceCountVO domainRC = null;
// calculate project count here
if (type == ResourceType.project) {
newResourceCount += _projectDao.countProjectsForDomain(domainId);
if (type == ResourceType.network) {
newResourceCount += networkDomainDao.listDomainNetworkMapByDomain(domainId).size();
}
// TODO make sure that the resource counts are not null
@ -1493,7 +1503,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
long totalTemplatesSize = 0;
SearchCriteria<SumCount> sc = templateSizeSearch.create();
sc.setParameters("downloadState", Status.DOWNLOADED);
sc.setParameters("downloadState", Status.DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS);
sc.setParameters("destroyed", false);
sc.setJoinParameters("templates", "accountId", accountId);
List<SumCount> templates = _vmTemplateStoreDao.customSearch(sc, null);
@ -1730,7 +1740,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
return tags;
}
protected List<String> getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering) {
@Override
public List<String> getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering) {
if (Boolean.FALSE.equals(display)) {
return new ArrayList<>();
}
@ -1744,54 +1755,53 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
}
@Override
public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException {
public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List<Reserver> reservations) throws ResourceAllocationException {
List<String> tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering);
if (CollectionUtils.isEmpty(tags)) {
return;
}
for (String tag : tags) {
checkResourceLimitWithTag(owner, ResourceType.volume, tag);
if (size != null) {
checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, size);
}
CheckedReservation volumeReservation = new CheckedReservation(owner, ResourceType.volume, tags, 1L, reservationDao, resourceLimitService);
reservations.add(volumeReservation);
if (size != null) {
CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, tags, size, reservationDao, resourceLimitService);
reservations.add(primaryStorageReservation);
}
}
@Override
public void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException {
public void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List<Reserver> reservations) throws ResourceAllocationException {
List<String> tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering);
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (size != null) {
for (String tag : tags) {
checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, size);
}
}
CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, tags, size, reservationDao, resourceLimitService);
reservations.add(primaryStorageReservation);
}
@Override
public void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize,
DiskOffering currentOffering, DiskOffering newOffering
DiskOffering currentOffering, DiskOffering newOffering, List<Reserver> reservations
) throws ResourceAllocationException {
Ternary<Set<String>, Set<String>, Set<String>> updatedResourceLimitStorageTags = getResourceLimitStorageTagsForDiskOfferingChange(display, currentOffering, newOffering);
if (updatedResourceLimitStorageTags == null) {
return;
}
Set<String> sameTags = updatedResourceLimitStorageTags.first();
Set<String> newTags = updatedResourceLimitStorageTags.second();
if (newSize > currentSize) {
for (String tag : sameTags) {
checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, newSize - currentSize);
}
List<String> currentTags = getResourceLimitStorageTagsForResourceCountOperation(true, currentOffering);
List<String> tagsAfterUpdate = getResourceLimitStorageTagsForResourceCountOperation(true, newOffering);
if (currentTags.isEmpty() && tagsAfterUpdate.isEmpty()) {
return;
}
for (String tag : newTags) {
checkResourceLimitWithTag(owner, ResourceType.volume, tag, 1L);
checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, newSize);
}
CheckedReservation volumeReservation = new CheckedReservation(owner, ResourceType.volume, null, tagsAfterUpdate,
currentTags, 1L, 1L, reservationDao, resourceLimitService);
reservations.add(volumeReservation);
CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, null,
tagsAfterUpdate, currentTags, newSize, currentSize, reservationDao, resourceLimitService);
reservations.add(primaryStorageReservation);
}
@DB
@ -2018,20 +2028,27 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
}
@Override
public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException {
public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, List<Reserver> reservations) throws ResourceAllocationException {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
}
CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, tags, 1L, reservationDao, resourceLimitService);
reservations.add(vmReservation);
Long cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L;
CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, tags, cpu, reservationDao, resourceLimitService);
reservations.add(cpuReservation);
Long ram = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L;
CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, tags, ram, reservationDao, resourceLimitService);
reservations.add(memReservation);
Long gpu = serviceOffering.getGpuCount() != null ? Long.valueOf(serviceOffering.getGpuCount()) : 0L;
for (String tag : tags) {
checkResourceLimitWithTag(owner, ResourceType.user_vm, tag);
checkResourceLimitWithTag(owner, ResourceType.cpu, tag, cpu);
checkResourceLimitWithTag(owner, ResourceType.memory, tag, ram);
checkResourceLimitWithTag(owner, ResourceType.gpu, tag, gpu);
}
CheckedReservation gpuReservation = new CheckedReservation(owner, ResourceType.gpu, tags, gpu, reservationDao, resourceLimitService);
reservations.add(gpuReservation);
}
@Override
@ -2081,83 +2098,60 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
@Override
public void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering,
VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate) throws ResourceAllocationException {
VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate, List<Reserver> reservations) throws ResourceAllocationException {
checkVmResourceLimitsForServiceOfferingAndTemplateChange(owner, display, null, null,
null, null, offering, offering, currentTemplate, newTemplate);
null, null, offering, offering, currentTemplate, newTemplate, reservations);
}
@Override
public void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu,
Long currentMemory, Long newMemory,
ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template
ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template, List<Reserver> reservations
) throws ResourceAllocationException {
checkVmResourceLimitsForServiceOfferingAndTemplateChange(owner, display, currentCpu, newCpu, currentMemory, newMemory, currentOffering,
newOffering != null ? newOffering : currentOffering, template, template);
newOffering != null ? newOffering : currentOffering, template, template, reservations);
}
private void checkVmResourceLimitsForServiceOfferingAndTemplateChange(Account owner, Boolean display, Long currentCpu, Long newCpu,
Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering,
VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate
VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate, List<Reserver> reservations
) throws ResourceAllocationException {
Ternary<Set<String>, Set<String>, Set<String>> updatedResourceLimitHostTags = getResourceLimitHostTagsForVmServiceOfferingAndTemplateChange(display, currentOffering, newOffering, currentTemplate, newTemplate);
if (updatedResourceLimitHostTags == null) {
List<String> currentTags = getResourceLimitHostTagsForResourceCountOperation(true, currentOffering, currentTemplate);
List<String> tagsAfterUpdate = getResourceLimitHostTagsForResourceCountOperation(true, newOffering, newTemplate);
if (currentTags.isEmpty() && tagsAfterUpdate.isEmpty()) {
return;
}
CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, null, tagsAfterUpdate,
currentTags, 1L, 1L, reservationDao, resourceLimitService);
reservations.add(vmReservation);
if (currentCpu == null) {
currentCpu = currentOffering.getCpu() != null ? Long.valueOf(currentOffering.getCpu()) : 0L;
}
if (newCpu == null) {
newCpu = newOffering.getCpu() != null ? Long.valueOf(newOffering.getCpu()) : 0L;
}
CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, null, tagsAfterUpdate,
currentTags, newCpu, currentCpu, reservationDao, resourceLimitService);
reservations.add(cpuReservation);
if (currentMemory == null) {
currentMemory = currentOffering.getRamSize() != null ? Long.valueOf(currentOffering.getRamSize()) : 0L;
}
if (newMemory == null) {
newMemory = newOffering.getRamSize() != null ? Long.valueOf(newOffering.getRamSize()) : 0L;
}
CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, null, tagsAfterUpdate,
currentTags, newMemory, currentMemory, reservationDao, resourceLimitService);
reservations.add(memReservation);
Long currentGpu = currentOffering.getGpuCount() != null ? Long.valueOf(currentOffering.getGpuCount()) : 0L;
Long newGpu = newOffering.getGpuCount() != null ? Long.valueOf(newOffering.getGpuCount()) : 0L;
Set<String> sameTags = updatedResourceLimitHostTags.first();
Set<String> newTags = updatedResourceLimitHostTags.second();
if (newCpu - currentCpu > 0 || newMemory - currentMemory > 0 || newGpu - currentGpu > 0) {
for (String tag : sameTags) {
if (newCpu - currentCpu > 0) {
checkResourceLimitWithTag(owner, ResourceType.cpu, tag, newCpu - currentCpu);
}
if (newMemory - currentMemory > 0) {
checkResourceLimitWithTag(owner, ResourceType.memory, tag, newMemory - currentMemory);
}
if (newGpu - currentGpu > 0) {
checkResourceLimitWithTag(owner, ResourceType.gpu, tag, newGpu - currentGpu);
}
}
}
for (String tag : newTags) {
checkResourceLimitWithTag(owner, ResourceType.user_vm, tag, 1L);
checkResourceLimitWithTag(owner, ResourceType.cpu, tag, newCpu);
checkResourceLimitWithTag(owner, ResourceType.memory, tag, newMemory);
checkResourceLimitWithTag(owner, ResourceType.gpu, tag, newGpu);
}
}
@Override
public void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (cpu == null) {
cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L;
}
for (String tag : tags) {
checkResourceLimitWithTag(owner, ResourceType.cpu, tag, cpu);
}
CheckedReservation gpuReservation = new CheckedReservation(owner, ResourceType.gpu, null, tagsAfterUpdate,
currentTags, newGpu, currentGpu, reservationDao, resourceLimitService);
reservations.add(gpuReservation);
}
@Override
@ -2188,20 +2182,6 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
}
}
@Override
public void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (memory == null) {
memory = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L;
}
for (String tag : tags) {
checkResourceLimitWithTag(owner, ResourceType.memory, tag, memory);
}
}
@Override
public void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
@ -2230,20 +2210,6 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
}
}
@Override
public void checkVmGpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu) throws ResourceAllocationException {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);
if (CollectionUtils.isEmpty(tags)) {
return;
}
if (gpu == null) {
gpu = serviceOffering.getGpuCount() != null ? Long.valueOf(serviceOffering.getGpuCount()) : 0L;
}
for (String tag : tags) {
checkResourceLimitWithTag(owner, ResourceType.gpu, tag, gpu);
}
}
@Override
public void incrementVmGpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu) {
List<String> tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template);

View File

@ -25,7 +25,6 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.agent.api.to.OVFInformationTO;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
@ -37,6 +36,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.cloudstack.storage.command.UploadStatusAnswer;
import org.apache.cloudstack.storage.command.UploadStatusAnswer.UploadStatus;
import org.apache.cloudstack.storage.command.UploadStatusCommand;
@ -55,6 +55,7 @@ import com.cloud.agent.api.AgentControlCommand;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.StartupCommand;
import com.cloud.agent.api.to.OVFInformationTO;
import com.cloud.alert.AlertManager;
import com.cloud.api.query.dao.TemplateJoinDao;
import com.cloud.api.query.vo.TemplateJoinVO;
@ -62,15 +63,20 @@ import com.cloud.configuration.Resource;
import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
import com.cloud.exception.ConnectionException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.host.Host;
import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.resourcelimit.CheckedReservation;
import com.cloud.storage.Volume.Event;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VMTemplateZoneDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.ResourceLimitService;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.Transaction;
@ -117,6 +123,12 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
private TemplateJoinDao templateJoinDao;
@Inject
private DeployAsIsHelper deployAsIsHelper;
@Inject
private AccountDao accountDao;
@Inject
private AccountManager _accountMgr;
@Inject
private ReservationDao reservationDao;
private long _nodeId;
private ScheduledExecutorService _executor = null;
@ -205,6 +217,36 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
public UploadStatusCheck() {
}
private Answer sendUploadStatusCommandForVolume(EndPoint ep, UploadStatusCommand cmd, VolumeVO volume) {
Answer answer = null;
try {
answer = ep.sendMessage(cmd);
} catch (CloudRuntimeException e) {
logger.warn("Unable to get upload status for volume {}. Error details: {}", volume, e.getMessage());
answer = new UploadStatusAnswer(cmd, UploadStatus.UNKNOWN, e.getMessage());
}
if (answer == null || !(answer instanceof UploadStatusAnswer)) {
logger.warn("No or invalid answer corresponding to UploadStatusCommand for volume {}", volume);
return null;
}
return answer;
}
private Answer sendUploadStatusCommandForTemplate(EndPoint ep, UploadStatusCommand cmd, VMTemplateVO template) {
Answer answer = null;
try {
answer = ep.sendMessage(cmd);
} catch (CloudRuntimeException e) {
logger.warn("Unable to get upload status for template {}. Error details: {}", template, e.getMessage());
answer = new UploadStatusAnswer(cmd, UploadStatus.UNKNOWN, e.getMessage());
}
if (answer == null || !(answer instanceof UploadStatusAnswer)) {
logger.warn("No or invalid answer corresponding to UploadStatusCommand for template {}", template);
return null;
}
return answer;
}
@Override
protected void runInContext() {
// 1. Select all entries with download_state = Not_Downloaded or Download_In_Progress
@ -231,18 +273,17 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
UploadStatusCommand cmd = new UploadStatusCommand(volume.getUuid(), EntityType.Volume);
if (host != null && host.getManagementServerId() != null) {
if (_nodeId == host.getManagementServerId().longValue()) {
Answer answer = null;
try {
answer = ep.sendMessage(cmd);
} catch (CloudRuntimeException e) {
logger.warn("Unable to get upload status for volume {}. Error details: {}", volume, e.getMessage());
answer = new UploadStatusAnswer(cmd, UploadStatus.UNKNOWN, e.getMessage());
}
if (answer == null || !(answer instanceof UploadStatusAnswer)) {
logger.warn("No or invalid answer corresponding to UploadStatusCommand for volume {}", volume);
Answer answer = sendUploadStatusCommandForVolume(ep, cmd, volume);
if (answer == null) {
continue;
}
handleVolumeStatusResponse((UploadStatusAnswer)answer, volume, volumeDataStore);
if (!handleVolumeStatusResponse((UploadStatusAnswer)answer, volume, volumeDataStore)) {
cmd = new UploadStatusCommand(volume.getUuid(), EntityType.Volume, true);
answer = sendUploadStatusCommandForVolume(ep, cmd, volume);
if (answer == null) {
logger.warn("Unable to abort upload for volume {}", volume);
}
}
}
} else {
String error = "Volume " + volume.getUuid() + " failed to upload as SSVM is either destroyed or SSVM agent not in 'Up' state";
@ -275,18 +316,17 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
UploadStatusCommand cmd = new UploadStatusCommand(template.getUuid(), EntityType.Template);
if (host != null && host.getManagementServerId() != null) {
if (_nodeId == host.getManagementServerId().longValue()) {
Answer answer = null;
try {
answer = ep.sendMessage(cmd);
} catch (CloudRuntimeException e) {
logger.warn("Unable to get upload status for template {}. Error details: {}", template, e.getMessage());
answer = new UploadStatusAnswer(cmd, UploadStatus.UNKNOWN, e.getMessage());
}
if (answer == null || !(answer instanceof UploadStatusAnswer)) {
logger.warn("No or invalid answer corresponding to UploadStatusCommand for template {}", template);
Answer answer = sendUploadStatusCommandForTemplate(ep, cmd, template);
if (answer == null) {
continue;
}
handleTemplateStatusResponse((UploadStatusAnswer)answer, template, templateDataStore);
if (!handleTemplateStatusResponse((UploadStatusAnswer) answer, template, templateDataStore)) {
cmd = new UploadStatusCommand(template.getUuid(), EntityType.Template, true);
answer = sendUploadStatusCommandForTemplate(ep, cmd, template);
if (answer == null) {
logger.warn("Unable to abort upload for template {}", template);
}
}
}
} else {
String error = String.format(
@ -303,7 +343,41 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
}
}
private void handleVolumeStatusResponse(final UploadStatusAnswer answer, final VolumeVO volume, final VolumeDataStoreVO volumeDataStore) {
private Boolean checkAndUpdateSecondaryStorageResourceLimit(Long accountId, Long lastSize, Long currentSize) {
if (lastSize >= currentSize) {
return true;
}
Long usage = currentSize - lastSize;
try (CheckedReservation secStorageReservation = new CheckedReservation(_accountMgr.getAccount(accountId), Resource.ResourceType.secondary_storage, null, null, usage, reservationDao, _resourceLimitMgr)) {
_resourceLimitMgr.incrementResourceCount(accountId, Resource.ResourceType.secondary_storage, usage);
return true;
} catch (Exception e) {
_resourceLimitMgr.decrementResourceCount(accountId, Resource.ResourceType.secondary_storage, lastSize);
return false;
}
}
private Boolean checkAndUpdateVolumeResourceLimit(VolumeVO volume, VolumeDataStoreVO volumeDataStore, UploadStatusAnswer answer) {
boolean success = true;
Long currentSize = answer.getVirtualSize() != 0 ? answer.getVirtualSize() : answer.getPhysicalSize();
Long lastSize = volume.getSize() != null ? volume.getSize() : 0L;
if (!checkAndUpdateSecondaryStorageResourceLimit(volume.getAccountId(), volume.getSize(), currentSize)) {
volumeDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR);
volumeDataStore.setState(State.Failed);
volumeDataStore.setErrorString("Storage Limit Reached");
Account owner = accountDao.findById(volume.getAccountId());
String msg = String.format("Upload of volume [%s] failed because its owner [%s] does not have enough secondary storage space available.", volume.getUuid(), owner.getUuid());
logger.error(msg);
success = false;
}
VolumeVO volumeUpdate = _volumeDao.findById(volume.getId());
volumeUpdate.setSize(currentSize);
_volumeDao.update(volumeUpdate.getId(), volumeUpdate);
return success;
}
private boolean handleVolumeStatusResponse(final UploadStatusAnswer answer, final VolumeVO volume, final VolumeDataStoreVO volumeDataStore) {
final boolean[] needAbort = new boolean[]{false};
final StateMachine2<Volume.State, Event, Volume> stateMachine = Volume.State.getStateMachine();
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
@ -315,6 +389,11 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
try {
switch (answer.getStatus()) {
case COMPLETED:
if (!checkAndUpdateVolumeResourceLimit(tmpVolume, tmpVolumeDataStore, answer)) {
stateMachine.transitTo(tmpVolume, Event.OperationFailed, null, _volumeDao);
sendAlert = true;
break;
}
tmpVolumeDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOADED);
tmpVolumeDataStore.setState(State.Ready);
tmpVolumeDataStore.setInstallPath(answer.getInstallPath());
@ -326,7 +405,6 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
volumeUpdate.setSize(answer.getVirtualSize());
_volumeDao.update(tmpVolume.getId(), volumeUpdate);
stateMachine.transitTo(tmpVolume, Event.OperationSucceeded, null, _volumeDao);
_resourceLimitMgr.incrementResourceCount(volume.getAccountId(), Resource.ResourceType.secondary_storage, answer.getVirtualSize());
// publish usage events
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_UPLOAD, tmpVolume.getAccountId(),
@ -339,6 +417,12 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
}
break;
case IN_PROGRESS:
if (!checkAndUpdateVolumeResourceLimit(tmpVolume, tmpVolumeDataStore, answer)) {
stateMachine.transitTo(tmpVolume, Event.OperationFailed, null, _volumeDao);
sendAlert = true;
needAbort[0] = true;
break;
}
if (tmpVolume.getState() == Volume.State.NotUploaded) {
tmpVolumeDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS);
tmpVolumeDataStore.setDownloadPercent(answer.getDownloadPercent());
@ -387,10 +471,29 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
}
}
});
return !needAbort[0];
}
private void handleTemplateStatusResponse(final UploadStatusAnswer answer, final VMTemplateVO template, final TemplateDataStoreVO templateDataStore) {
private Boolean checkAndUpdateTemplateResourceLimit(VMTemplateVO template, TemplateDataStoreVO templateDataStore, UploadStatusAnswer answer) {
boolean success = true;
Long currentSize = answer.getVirtualSize() != 0 ? answer.getVirtualSize() : answer.getPhysicalSize();
Long lastSize = template.getSize() != null ? template.getSize() : 0L;
if (!checkAndUpdateSecondaryStorageResourceLimit(template.getAccountId(), lastSize, currentSize)) {
templateDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR);
templateDataStore.setErrorString("Storage Limit Reached");
templateDataStore.setState(State.Failed);
Account owner = accountDao.findById(template.getAccountId());
String msg = String.format("Upload of template [%s] failed because its owner [%s] does not have enough secondary storage space available.", template.getUuid(), owner.getUuid());
logger.error(msg);
success = false;
}
templateDataStore.setSize(currentSize);
return success;
}
private boolean handleTemplateStatusResponse(final UploadStatusAnswer answer, final VMTemplateVO template, final TemplateDataStoreVO templateDataStore) {
final StateMachine2<VirtualMachineTemplate.State, VirtualMachineTemplate.Event, VirtualMachineTemplate> stateMachine = VirtualMachineTemplate.State.getStateMachine();
final boolean[] needAbort = new boolean[]{false};
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
@ -401,6 +504,11 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
try {
switch (answer.getStatus()) {
case COMPLETED:
if (!checkAndUpdateTemplateResourceLimit(tmpTemplate, tmpTemplateDataStore, answer)) {
stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationFailed, null, _templateDao);
sendAlert = true;
break;
}
tmpTemplateDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOADED);
tmpTemplateDataStore.setState(State.Ready);
tmpTemplateDataStore.setInstallPath(answer.getInstallPath());
@ -436,8 +544,23 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
break;
}
}
Account owner = accountDao.findById(template.getAccountId());
long templateSize = answer.getVirtualSize();
try (CheckedReservation secondaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.secondary_storage, null, null, templateSize, reservationDao, _resourceLimitMgr)) {
_resourceLimitMgr.incrementResourceCount(owner.getId(), Resource.ResourceType.secondary_storage, templateSize);
} catch (ResourceAllocationException e) {
tmpTemplateDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.UPLOAD_ERROR);
tmpTemplateDataStore.setState(State.Failed);
stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationFailed, null, _templateDao);
msg = String.format("Upload of template [%s] failed because its owner [%s] does not have enough secondary storage space available.", template.getUuid(), owner.getUuid());
logger.warn(msg);
sendAlert = true;
break;
}
stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationSucceeded, null, _templateDao);
_resourceLimitMgr.incrementResourceCount(template.getAccountId(), Resource.ResourceType.secondary_storage, answer.getVirtualSize());
//publish usage event
String etype = EventTypes.EVENT_TEMPLATE_CREATE;
if (tmpTemplate.getFormat() == Storage.ImageFormat.ISO) {
@ -453,6 +576,12 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
}
break;
case IN_PROGRESS:
if (!checkAndUpdateTemplateResourceLimit(tmpTemplate, tmpTemplateDataStore, answer)) {
stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationFailed, null, _templateDao);
sendAlert = true;
needAbort[0] = true;
break;
}
if (tmpTemplate.getState() == VirtualMachineTemplate.State.NotUploaded) {
tmpTemplateDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS);
stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.UploadRequested, null, _templateDao);
@ -502,6 +631,7 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto
}
}
});
return !needAbort[0];
}
}

View File

@ -95,6 +95,11 @@ public abstract class DownloadActiveState extends DownloadState {
return Status.ABANDONED.toString();
}
@Override
public String handleLimitReached() {
return Status.LIMIT_REACHED.toString();
}
@Override
public String handleDisconnect() {

View File

@ -60,6 +60,11 @@ public class DownloadErrorState extends DownloadInactiveState {
return Status.ABANDONED.toString();
}
@Override
public String handleLimitReached() {
return Status.LIMIT_REACHED.toString();
}
@Override
public String getName() {
return Status.DOWNLOAD_ERROR.toString();

View File

@ -36,6 +36,12 @@ public abstract class DownloadInactiveState extends DownloadState {
return getName();
}
@Override
public String handleLimitReached() {
// ignore and stay put
return getName();
}
@Override
public String handleDisconnect() {
//ignore and stay put

View File

@ -0,0 +1,54 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.storage.download;
import org.apache.cloudstack.storage.command.DownloadProgressCommand.RequestType;
import org.apache.logging.log4j.Level;
import com.cloud.agent.api.storage.DownloadAnswer;
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
public class DownloadLimitReachedState extends DownloadInactiveState {
public DownloadLimitReachedState(DownloadListener dl) {
super(dl);
}
@Override
public String getName() {
return Status.LIMIT_REACHED.toString();
}
@Override
public String handleEvent(DownloadEvent event, Object eventObj) {
if (logger.isTraceEnabled()) {
getDownloadListener().log("handleEvent, event type=" + event + ", curr state=" + getName(), Level.TRACE);
}
return Status.LIMIT_REACHED.toString();
}
@Override
public void onEntry(String prevState, DownloadEvent event, Object evtObj) {
if (!prevState.equalsIgnoreCase(getName())) {
DownloadAnswer answer = new DownloadAnswer("Storage Limit Reached", Status.LIMIT_REACHED);
getDownloadListener().callback(answer);
getDownloadListener().cancelStatusTask();
getDownloadListener().cancelTimeoutTask();
getDownloadListener().scheduleImmediateStatusCheck(RequestType.PURGE);
}
}
}

View File

@ -25,6 +25,15 @@ import java.util.Timer;
import javax.inject.Inject;
import com.cloud.configuration.Resource;
import com.cloud.resourcelimit.CheckedReservation;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.ResourceLimitService;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
@ -34,10 +43,13 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.cloudstack.storage.command.DownloadCommand;
import org.apache.cloudstack.storage.command.DownloadCommand.ResourceType;
import org.apache.cloudstack.storage.command.DownloadProgressCommand;
import org.apache.cloudstack.storage.command.DownloadProgressCommand.RequestType;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.utils.cache.LazyCache;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
@ -107,6 +119,7 @@ public class DownloadListener implements Listener {
public static final String DOWNLOAD_ERROR = Status.DOWNLOAD_ERROR.toString();
public static final String DOWNLOAD_IN_PROGRESS = Status.DOWNLOAD_IN_PROGRESS.toString();
public static final String DOWNLOAD_ABANDONED = Status.ABANDONED.toString();
public static final String DOWNLOAD_LIMIT_REACHED = Status.LIMIT_REACHED.toString();
private EndPoint _ssAgent;
@ -137,6 +150,18 @@ public class DownloadListener implements Listener {
private DataStoreManager _storeMgr;
@Inject
private VolumeService _volumeSrv;
@Inject
private VMTemplateDao _templateDao;
@Inject
private TemplateDataStoreDao _templateDataStoreDao;
@Inject
private VolumeDao _volumeDao;
@Inject
private ResourceLimitService _resourceLimitMgr;
@Inject
private AccountManager _accountMgr;
@Inject
ReservationDao _reservationDao;
private LazyCache<Long, List<Hypervisor.HypervisorType>> zoneHypervisorsCache;
@ -160,7 +185,7 @@ public class DownloadListener implements Listener {
_downloadMonitor = downloadMonitor;
_cmd = cmd;
initStateMachine();
_currState = getState(Status.NOT_DOWNLOADED.toString());
_currState = getState(NOT_DOWNLOADED);
this._timer = timer;
_timeoutTask = new TimeoutTask(this);
this._timer.schedule(_timeoutTask, 3 * STATUS_POLL_INTERVAL);
@ -184,11 +209,12 @@ public class DownloadListener implements Listener {
}
private void initStateMachine() {
_stateMap.put(Status.NOT_DOWNLOADED.toString(), new NotDownloadedState(this));
_stateMap.put(Status.DOWNLOADED.toString(), new DownloadCompleteState(this));
_stateMap.put(Status.DOWNLOAD_ERROR.toString(), new DownloadErrorState(this));
_stateMap.put(Status.DOWNLOAD_IN_PROGRESS.toString(), new DownloadInProgressState(this));
_stateMap.put(Status.ABANDONED.toString(), new DownloadAbandonedState(this));
_stateMap.put(NOT_DOWNLOADED, new NotDownloadedState(this));
_stateMap.put(DOWNLOADED, new DownloadCompleteState(this));
_stateMap.put(DOWNLOAD_ERROR, new DownloadErrorState(this));
_stateMap.put(DOWNLOAD_IN_PROGRESS, new DownloadInProgressState(this));
_stateMap.put(DOWNLOAD_ABANDONED, new DownloadAbandonedState(this));
_stateMap.put(DOWNLOAD_LIMIT_REACHED, new DownloadLimitReachedState(this));
}
private DownloadState getState(String stateName) {
@ -239,10 +265,53 @@ public class DownloadListener implements Listener {
return false;
}
private Long getAccountIdForDataObject() {
if (object == null) {
return null;
}
if (DataObjectType.TEMPLATE.equals(object.getType())) {
VMTemplateVO t = _templateDao.findById(object.getId());
return t != null ? t.getAccountId() : null;
} else if (DataObjectType.VOLUME.equals(object.getType())) {
VolumeVO v = _volumeDao.findById(object.getId());
return v != null ? v.getAccountId() : null;
}
return null;
}
private Long getSizeFromDB() {
Long lastSize = 0L;
if (DataObjectType.TEMPLATE.equals(object.getType())) {
TemplateDataStoreVO t = _templateDataStoreDao.findByStoreTemplate(object.getDataStore().getId(), object.getId());
lastSize = t.getSize();
} else if (DataObjectType.VOLUME.equals(object.getType())) {
VolumeVO v = _volumeDao.findById(object.getId());
lastSize = v.getSize();
}
return lastSize;
}
private Boolean checkAndUpdateResourceLimits(DownloadAnswer answer) {
Long lastSize = getSizeFromDB();
Long currentSize = answer.getTemplateSize();
if (currentSize > lastSize) {
Long accountId = getAccountIdForDataObject();
Account account = _accountMgr.getAccount(accountId);
Long usage = currentSize - lastSize;
try (CheckedReservation secStorageReservation = new CheckedReservation(account, Resource.ResourceType.secondary_storage, usage, _reservationDao, _resourceLimitMgr)) {
_resourceLimitMgr.incrementResourceCount(accountId, Resource.ResourceType.secondary_storage, usage);
} catch (Exception e) {
return false;
}
}
return true;
}
@Override
public boolean processAnswers(long agentId, long seq, Answer[] answers) {
boolean processed = false;
if (answers != null & answers.length > 0) {
if (answers != null && answers.length > 0) {
if (answers[0] instanceof DownloadAnswer) {
final DownloadAnswer answer = (DownloadAnswer)answers[0];
if (getJobId() == null) {
@ -250,7 +319,11 @@ public class DownloadListener implements Listener {
} else if (!getJobId().equalsIgnoreCase(answer.getJobId())) {
return false;//TODO
}
transition(DownloadEvent.DOWNLOAD_ANSWER, answer);
if (!checkAndUpdateResourceLimits(answer)) {
transition(DownloadEvent.LIMIT_REACHED, answer);
} else {
transition(DownloadEvent.DOWNLOAD_ANSWER, answer);
}
processed = true;
}
}

View File

@ -26,7 +26,7 @@ import com.cloud.agent.api.storage.DownloadAnswer;
public abstract class DownloadState {
public static enum DownloadEvent {
DOWNLOAD_ANSWER, ABANDON_DOWNLOAD, TIMEOUT_CHECK, DISCONNECT
DOWNLOAD_ANSWER, ABANDON_DOWNLOAD, LIMIT_REACHED, TIMEOUT_CHECK, DISCONNECT
};
protected Logger logger = LogManager.getLogger(getClass());
@ -51,6 +51,8 @@ public abstract class DownloadState {
return handleAnswer(answer);
case ABANDON_DOWNLOAD:
return handleAbort();
case LIMIT_REACHED:
return handleLimitReached();
case TIMEOUT_CHECK:
Date now = new Date();
long update = now.getTime() - dl.getLastUpdated().getTime();
@ -78,6 +80,8 @@ public abstract class DownloadState {
public abstract String handleAbort();
public abstract String handleLimitReached();
public abstract String handleDisconnect();
public abstract String handleAnswer(DownloadAnswer answer);

View File

@ -16,8 +16,6 @@
// under the License.
package com.cloud.storage.snapshot;
import com.cloud.storage.StoragePoolStatus;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -36,9 +34,6 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.host.dao.HostDao;
import com.cloud.storage.Upload;
import com.cloud.storage.dao.SnapshotDetailsDao;
import org.apache.cloudstack.acl.SecurityChecker;
import com.cloud.api.ApiDBUtils;
import org.apache.cloudstack.annotation.AnnotationService;
@ -77,6 +72,7 @@ import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.jobs.AsyncJob;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.cloudstack.resourcedetail.SnapshotPolicyDetailVO;
import org.apache.cloudstack.resourcedetail.dao.SnapshotPolicyDetailsDao;
import org.apache.cloudstack.snapshot.SnapshotHelper;
@ -119,10 +115,12 @@ import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.exception.StorageUnavailableException;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.org.Grouping;
import com.cloud.projects.Project.ListProjectResourcesCriteria;
import com.cloud.resource.ResourceManager;
import com.cloud.resourcelimit.CheckedReservation;
import com.cloud.server.ResourceTag.ResourceObjectType;
import com.cloud.server.TaggedResourceService;
import com.cloud.storage.CreateSnapshotPayload;
@ -135,15 +133,18 @@ import com.cloud.storage.SnapshotPolicyVO;
import com.cloud.storage.SnapshotScheduleVO;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.Storage;
import com.cloud.storage.StoragePoolStatus;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.Upload;
import com.cloud.storage.VMTemplateStorageResourceAssoc;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.SnapshotDetailsDao;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.SnapshotPolicyDao;
import com.cloud.storage.dao.SnapshotScheduleDao;
@ -251,7 +252,8 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
public TaggedResourceService taggedResourceService;
@Inject
private AnnotationDao annotationDao;
@Inject
private ReservationDao reservationDao;
@Inject
protected SnapshotHelper snapshotHelper;
@Inject
@ -321,7 +323,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
hostIdsToTryFirst = new long[] {vmHostId};
}
List<Long> hostIdsToAvoid = new ArrayList<Long>();
List<Long> hostIdsToAvoid = new ArrayList<>();
for (int retry = _totalRetries; retry >= 0; retry--) {
try {
Pair<Long, Answer> result = _storageMgr.sendToPool(pool, hostIdsToTryFirst, hostIdsToAvoid, cmd);
@ -421,7 +423,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
}
public void updateVolumeSizeAndPrimaryStorageCount(VolumeVO volume, SnapshotVO snapshot) {
Long differenceBetweenVolumeAndSnapshotSize = new Long(volume.getSize() - snapshot.getSize());
long differenceBetweenVolumeAndSnapshotSize = volume.getSize() - snapshot.getSize();
if (differenceBetweenVolumeAndSnapshotSize != 0) {
if (differenceBetweenVolumeAndSnapshotSize > 0) {
_resourceLimitMgr.decrementResourceCount(snapshot.getAccountId(), ResourceType.primary_storage, differenceBetweenVolumeAndSnapshotSize);
@ -696,7 +698,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
} catch (Exception e) {
logger.debug("Failed to backup Snapshot from Instance Snapshot", e);
_resourceLimitMgr.decrementResourceCount(snapshotOwnerId, ResourceType.snapshot);
_resourceLimitMgr.decrementResourceCount(snapshotOwnerId, ResourceType.secondary_storage, new Long(volume.getSize()));
_resourceLimitMgr.decrementResourceCount(snapshotOwnerId, ResourceType.secondary_storage, volume.getSize());
throw new CloudRuntimeException("Failed to backup Snapshot from Instance Snapshot", e);
} finally {
if (snapshotOnPrimaryStore != null) {
@ -911,43 +913,36 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
List<SnapshotDataStoreVO> snapshotStoreRefs = storeRefAndZones.first();
List<Long> zoneIds = storeRefAndZones.second();
boolean result = snapshotStrategy.deleteSnapshot(snapshotId, zoneId);
if (result) {
for (Long zId : zoneIds) {
if (snapshotCheck.getState() == Snapshot.State.BackedUp) {
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_DELETE, snapshotCheck.getAccountId(), zId, snapshotId,
snapshotCheck.getName(), null, null, 0L, snapshotCheck.getClass().getName(), snapshotCheck.getUuid());
try {
boolean result = snapshotStrategy.deleteSnapshot(snapshotId, zoneId);
if (result) {
for (Long zId : zoneIds) {
if (snapshotCheck.getState() == Snapshot.State.BackedUp) {
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_DELETE, snapshotCheck.getAccountId(), zId, snapshotId,
snapshotCheck.getName(), null, null, 0L, snapshotCheck.getClass().getName(), snapshotCheck.getUuid());
}
}
final SnapshotVO postDeleteSnapshotEntry = _snapshotDao.findById(snapshotId);
if (postDeleteSnapshotEntry == null || Snapshot.State.Destroyed.equals(postDeleteSnapshotEntry.getState())) {
annotationDao.removeByEntityType(AnnotationService.EntityType.SNAPSHOT.name(), snapshotCheck.getUuid());
if (snapshotCheck.getState() != Snapshot.State.Error && snapshotCheck.getState() != Snapshot.State.Destroyed) {
_resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.snapshot);
}
}
for (SnapshotDataStoreVO snapshotStoreRef : snapshotStoreRefs) {
if (ObjectInDataStoreStateMachine.State.Ready.equals(snapshotStoreRef.getState()) && !DataStoreRole.Primary.equals(snapshotStoreRef.getRole())) {
_resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.secondary_storage, snapshotStoreRef.getPhysicalSize());
}
}
}
final SnapshotVO postDeleteSnapshotEntry = _snapshotDao.findById(snapshotId);
if (postDeleteSnapshotEntry == null || Snapshot.State.Destroyed.equals(postDeleteSnapshotEntry.getState())) {
annotationDao.removeByEntityType(AnnotationService.EntityType.SNAPSHOT.name(), snapshotCheck.getUuid());
if (snapshotCheck.getState() != Snapshot.State.Error && snapshotCheck.getState() != Snapshot.State.Destroyed) {
_resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.snapshot);
}
}
for (SnapshotDataStoreVO snapshotStoreRef : snapshotStoreRefs) {
if (ObjectInDataStoreStateMachine.State.Ready.equals(snapshotStoreRef.getState()) && !DataStoreRole.Primary.equals(snapshotStoreRef.getRole())) {
_resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.secondary_storage, new Long(snapshotStoreRef.getPhysicalSize()));
}
}
}
final SnapshotVO postDeleteSnapshotEntry = _snapshotDao.findById(snapshotId);
if (postDeleteSnapshotEntry == null || Snapshot.State.Destroyed.equals(postDeleteSnapshotEntry.getState())) {
annotationDao.removeByEntityType(AnnotationService.EntityType.SNAPSHOT.name(), snapshotCheck.getUuid());
return result;
} catch (Exception e) {
logger.debug("Failed to delete snapshot {}:{}", snapshotCheck, e.toString());
if (snapshotCheck.getState() != Snapshot.State.Error && snapshotCheck.getState() != Snapshot.State.Destroyed) {
_resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.snapshot);
}
throw new CloudRuntimeException("Failed to delete snapshot:" + e.toString());
}
for (SnapshotDataStoreVO snapshotStoreRef : snapshotStoreRefs) {
if (ObjectInDataStoreStateMachine.State.Ready.equals(snapshotStoreRef.getState()) && !DataStoreRole.Primary.equals(snapshotStoreRef.getRole())) {
_resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.secondary_storage, new Long(snapshotStoreRef.getPhysicalSize()));
}
}
return result;
}
@Override
@ -961,7 +956,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
Map<String, String> tags = cmd.getTags();
Long zoneId = cmd.getZoneId();
Account caller = CallContext.current().getCallingAccount();
List<Long> permittedAccounts = new ArrayList<Long>();
List<Long> permittedAccounts = new ArrayList<>();
// Verify parameters
if (volumeId != null) {
@ -973,7 +968,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds());
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(),
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(),
cmd.isRecursive(), null);
_accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false);
Long domainId = domainIdRecursiveListProject.first();
@ -1066,7 +1061,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
}
Pair<List<SnapshotVO>, Integer> result = _snapshotDao.searchAndCount(sc, searchFilter);
return new Pair<List<? extends Snapshot>, Integer>(result.first(), result.second());
return new Pair<>(result.first(), result.second());
}
@Override
@ -1082,7 +1077,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
if (volume.getPoolId() == null) {
continue;
}
Long volumeId = volume.getId();
long volumeId = volume.getId();
Long dcId = volume.getDataCenterId();
if (_snapshotDao.listByVolumeIdIncludingRemoved(volumeId).isEmpty()) {
// This volume doesn't have any snapshots. Nothing do delete.
@ -1126,7 +1121,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
if (Type.MANUAL == snapshot.getRecurringType()) {
_resourceLimitMgr.decrementResourceCount(accountId, ResourceType.snapshot);
for (SnapshotDataStoreVO snapshotStoreRef : snapshotStoreRefs) {
_resourceLimitMgr.decrementResourceCount(accountId, ResourceType.secondary_storage, new Long(snapshotStoreRef.getPhysicalSize()));
_resourceLimitMgr.decrementResourceCount(accountId, ResourceType.secondary_storage, snapshotStoreRef.getPhysicalSize());
}
}
@ -1155,7 +1150,6 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
throw new InvalidParameterValueException("Backing up of snapshot is not supported by the zone of the volume. Snapshots can not be taken for multiple zones");
}
boolean isRootAdminCaller = _accountMgr.isRootAdmin(caller.getId());
if (hasZones) {
for (Long zoneId : zoneIds) {
getCheckedDestinationZoneForSnapshotCopy(zoneId, isRootAdminCaller);
@ -1291,7 +1285,6 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
logger.debug("Acquired lock for creating snapshot policy [{}] for volume {}.", intervalType, volume);
try {
SnapshotPolicyVO policy = _snapshotPolicyDao.findOneByVolumeInterval(volumeId, intervalType);
@ -1498,7 +1491,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
}
// List only future schedules, not past ones.
List<SnapshotScheduleVO> snapshotSchedules = new ArrayList<SnapshotScheduleVO>();
List<SnapshotScheduleVO> snapshotSchedules = new ArrayList<>();
if (policyId == null) {
List<SnapshotPolicyVO> policyInstances = listPoliciesforVolume(volumeId);
for (SnapshotPolicyVO policyInstance : policyInstances) {
@ -1632,7 +1625,6 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
Long snapshotId = payload.getSnapshotId();
Account snapshotOwner = payload.getAccount();
SnapshotInfo snapshot = snapshotFactory.getSnapshot(snapshotId, volume.getDataStore());
StoragePool storagePool = _storagePoolDao.findById(volume.getPoolId());
@ -1698,7 +1690,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
ResourceType storeResourceType = dataStoreRole == DataStoreRole.Image ? ResourceType.secondary_storage : ResourceType.primary_storage;
// Correct the resource count of snapshot in case of delta snapshots.
_resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), storeResourceType, new Long(volume.getSize() - snapshotStoreRef.getPhysicalSize()));
_resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), storeResourceType, volume.getSize() - snapshotStoreRef.getPhysicalSize());
if (!payload.getAsyncBackup()) {
if (backupSnapToSecondary) {
@ -1717,7 +1709,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
}
ResourceType storeResourceType = getStoreResourceType(volume.getDataCenterId(), payload.getLocationType());
_resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot);
_resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), storeResourceType, new Long(volume.getSize()));
_resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), storeResourceType, volume.getSize());
throw cre;
} catch (Exception e) {
if (logger.isDebugEnabled()) {
@ -1725,7 +1717,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
}
ResourceType storeResourceType = getStoreResourceType(volume.getDataCenterId(), payload.getLocationType());
_resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot);
_resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), storeResourceType, new Long(volume.getSize()));
_resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), storeResourceType, volume.getSize());
throw new CloudRuntimeException("Failed to create snapshot", e);
}
return snapshot;
@ -1935,7 +1927,7 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
}
if (policyIds == null) {
policyIds = new ArrayList<Long>();
policyIds = new ArrayList<>();
policyIds.add(policyId);
} else if (policyIds.size() <= 0) {
// Not even sure how this is even possible
@ -2018,20 +2010,6 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
Type snapshotType = getSnapshotType(policyId);
Account owner = _accountMgr.getAccount(volume.getAccountId());
ResourceType storeResourceType = getStoreResourceType(volume.getDataCenterId(), locationType);
try {
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.snapshot);
_resourceLimitMgr.checkResourceLimit(owner, storeResourceType, volume.getSize());
} catch (ResourceAllocationException e) {
if (snapshotType != Type.MANUAL) {
String msg = String.format("Snapshot resource limit exceeded for account %s. Failed to create recurring snapshots", owner);
logger.warn(msg);
_alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, "Snapshot resource limit exceeded for account id : " + owner.getId()
+ ". Failed to create recurring snapshots; please use updateResourceLimit to increase the limit");
}
throw e;
}
// Determine the name for this snapshot
// Snapshot Name: VMInstancename + volumeName + timeString
String timeString = DateUtil.getDateDisplayString(DateUtil.GMT_TIMEZONE, new Date(), DateUtil.YYYYMMDD_FORMAT);
@ -2063,6 +2041,14 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
hypervisorType = volume.getHypervisorType();
}
ResourceType storeResourceType = ResourceType.secondary_storage;
if (!isBackupSnapshotToSecondaryForZone(volume.getDataCenterId()) ||
Snapshot.LocationType.PRIMARY.equals(locationType)) {
storeResourceType = ResourceType.primary_storage;
}
try (CheckedReservation volumeSnapshotReservation = new CheckedReservation(owner, ResourceType.snapshot, null, null, 1L, reservationDao, _resourceLimitMgr);
CheckedReservation storageReservation = new CheckedReservation(owner, storeResourceType, null, null, volume.getSize(), reservationDao, _resourceLimitMgr)) {
SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName,
(short)snapshotType.ordinal(), snapshotType.name(), volume.getSize(), volume.getMinIops(), volume.getMaxIops(), hypervisorType, locationType);
@ -2074,6 +2060,14 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
_resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.snapshot);
_resourceLimitMgr.incrementResourceCount(volume.getAccountId(), storeResourceType, volume.getSize());
return snapshot;
} catch (ResourceAllocationException e) {
if (snapshotType != Type.MANUAL) {
String msg = String.format("Snapshot resource limit exceeded for account id : %s. Failed to create recurring snapshots", owner.getId());
logger.warn(msg);
_alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, msg + ". Please, use updateResourceLimit to increase the limit");
}
throw e;
}
}
@Override
@ -2123,57 +2117,61 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
@DB
private boolean copySnapshotToZone(SnapshotDataStoreVO snapshotDataStoreVO, DataStore srcSecStore,
DataCenterVO dstZone, DataStore dstSecStore, Account account, boolean kvmIncrementalSnapshot)
DataCenterVO dstZone, DataStore dstSecStore, Account account, boolean kvmIncrementalSnapshot, boolean shouldCheckResourceLimits)
throws ResourceAllocationException {
final long snapshotId = snapshotDataStoreVO.getSnapshotId();
final long dstZoneId = dstZone.getId();
if (checkAndProcessSnapshotAlreadyExistInStore(snapshotId, dstSecStore)) {
return true;
}
_resourceLimitMgr.checkResourceLimit(account, ResourceType.secondary_storage, snapshotDataStoreVO.getSize());
// snapshotId may refer to ID of a removed parent snapshot
SnapshotInfo snapshotOnSecondary = snapshotFactory.getSnapshot(snapshotId, srcSecStore);
if (kvmIncrementalSnapshot) {
snapshotOnSecondary = snapshotHelper.convertSnapshotIfNeeded(snapshotOnSecondary);
}
String copyUrl = null;
try {
AsyncCallFuture<CreateCmdResult> future = snapshotSrv.queryCopySnapshot(snapshotOnSecondary);
CreateCmdResult result = future.get();
if (!result.isFailed()) {
copyUrl = result.getPath();
// Resource limit checks are not performed here at the moment, but they were added in case this method is used
// in the future to copy a standalone snapshot
long requiredSecondaryStorageSpace = shouldCheckResourceLimits ? snapshotDataStoreVO.getSize() : 0L;
try (CheckedReservation secondaryStorageReservation = new CheckedReservation(account, ResourceType.secondary_storage, null, null, null, requiredSecondaryStorageSpace, null, reservationDao, _resourceLimitMgr)) {
// snapshotId may refer to ID of a removed parent snapshot
SnapshotInfo snapshotOnSecondary = snapshotFactory.getSnapshot(snapshotId, srcSecStore);
if (kvmIncrementalSnapshot) {
snapshotOnSecondary = snapshotHelper.convertSnapshotIfNeeded(snapshotOnSecondary);
}
} catch (InterruptedException | ExecutionException | ResourceUnavailableException ex) {
logger.error("Failed to prepare URL for copy for snapshot ID: {} on store: {}", snapshotId, srcSecStore, ex);
}
if (StringUtils.isEmpty(copyUrl)) {
logger.error("Unable to prepare URL for copy for snapshot ID: {} on store: {}", snapshotId, srcSecStore);
return false;
}
logger.debug(String.format("Copying snapshot ID: %d to destination zones using download URL: %s", snapshotId, copyUrl));
try {
AsyncCallFuture<SnapshotResult> future = snapshotSrv.copySnapshot(snapshotOnSecondary, copyUrl, dstSecStore);
SnapshotResult result = future.get();
if (result.isFailed()) {
logger.debug("Copy snapshot ID: {} failed for image store {}: {}", snapshotId, dstSecStore, result.getResult());
String copyUrl = null;
try {
AsyncCallFuture<CreateCmdResult> future = snapshotSrv.queryCopySnapshot(snapshotOnSecondary);
CreateCmdResult result = future.get();
if (!result.isFailed()) {
copyUrl = result.getPath();
}
} catch (InterruptedException | ExecutionException | ResourceUnavailableException ex) {
logger.error("Failed to prepare URL for copy for snapshot ID: {} on store: {}", snapshotId, srcSecStore, ex);
}
if (StringUtils.isEmpty(copyUrl)) {
logger.error("Unable to prepare URL for copy for snapshot ID: {} on store: {}", snapshotId, srcSecStore);
return false;
}
snapshotZoneDao.addSnapshotToZone(snapshotId, dstZoneId);
_resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.secondary_storage, snapshotDataStoreVO.getSize());
if (account.getId() != Account.ACCOUNT_ID_SYSTEM) {
SnapshotVO snapshotVO = _snapshotDao.findByIdIncludingRemoved(snapshotId);
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_COPY, account.getId(), dstZoneId, snapshotId, null, null, null, snapshotVO.getSize(),
snapshotVO.getSize(), snapshotVO.getClass().getName(), snapshotVO.getUuid());
}
if (kvmIncrementalSnapshot) {
((ImageStoreEntity) srcSecStore).deleteExtractUrl(snapshotOnSecondary.getPath(), copyUrl, Upload.Type.SNAPSHOT);
}
logger.debug(String.format("Copying snapshot ID: %d to destination zones using download URL: %s", snapshotId, copyUrl));
try {
AsyncCallFuture<SnapshotResult> future = snapshotSrv.copySnapshot(snapshotOnSecondary, copyUrl, dstSecStore);
SnapshotResult result = future.get();
if (result.isFailed()) {
logger.debug("Copy snapshot ID: {} failed for image store {}: {}", snapshotId, dstSecStore, result.getResult());
return false;
}
snapshotZoneDao.addSnapshotToZone(snapshotId, dstZoneId);
_resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.secondary_storage, snapshotDataStoreVO.getSize());
if (account.getId() != Account.ACCOUNT_ID_SYSTEM) {
SnapshotVO snapshotVO = _snapshotDao.findByIdIncludingRemoved(snapshotId);
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_COPY, account.getId(), dstZoneId, snapshotId, null, null, null, snapshotVO.getSize(),
snapshotVO.getSize(), snapshotVO.getClass().getName(), snapshotVO.getUuid());
}
if (kvmIncrementalSnapshot) {
((ImageStoreEntity) srcSecStore).deleteExtractUrl(snapshotOnSecondary.getPath(), copyUrl, Upload.Type.SNAPSHOT);
}
return true;
} catch (InterruptedException | ExecutionException | ResourceUnavailableException ex) {
logger.debug("Failed to copy snapshot ID: {} to image store: {}", snapshotId, dstSecStore);
return true;
} catch (InterruptedException | ExecutionException | ResourceUnavailableException ex) {
logger.debug("Failed to copy snapshot ID: {} to image store: {}", snapshotId, dstSecStore);
}
return false;
}
return false;
}
@DB
@ -2205,13 +2203,6 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
if (CollectionUtils.isEmpty(snapshotChain)) {
return true;
}
try {
_resourceLimitMgr.checkResourceLimit(account, ResourceType.secondary_storage, size);
} catch (ResourceAllocationException e) {
logger.error(String.format("Unable to allocate secondary storage resources for snapshot chain for %s with size: %d", snapshotVO, size), e);
return false;
}
Collections.reverse(snapshotChain);
if (dstSecStore == null) {
// find all eligible image stores for the destination zone
List<DataStore> dstSecStores = dataStoreMgr.getImageStoresByScopeExcludingReadOnly(new ZoneScope(destZoneId));
@ -2223,16 +2214,23 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement
throw new StorageUnavailableException("Destination zone is not ready, no image store with free capacity", DataCenter.class, destZoneId);
}
}
logger.debug("Copying snapshot chain for snapshot ID: {} on secondary store: {} of zone ID: {}", snapshotVO, dstSecStore, destZone);
for (SnapshotDataStoreVO snapshotDataStoreVO : snapshotChain) {
if (!copySnapshotToZone(snapshotDataStoreVO, srcSecStore, destZone, dstSecStore, account, kvmIncrementalSnapshot)) {
logger.error("Failed to copy snapshot: {} to zone: {} due to failure to copy snapshot ID: {} from snapshot chain",
snapshotVO, destZone, snapshotDataStoreVO.getSnapshotId());
return false;
try (CheckedReservation secondaryStorageReservation = new CheckedReservation(account, ResourceType.secondary_storage, null, null, null, size, null, reservationDao, _resourceLimitMgr)) {
logger.debug("Copying snapshot chain for snapshot ID: {} on secondary store: {} of zone ID: {}", snapshotVO, dstSecStore, destZone);
Collections.reverse(snapshotChain);
for (SnapshotDataStoreVO snapshotDataStoreVO : snapshotChain) {
if (!copySnapshotToZone(snapshotDataStoreVO, srcSecStore, destZone, dstSecStore, account, kvmIncrementalSnapshot, false)) {
logger.error("Failed to copy snapshot: {} to zone: {} due to failure to copy snapshot ID: {} from snapshot chain",
snapshotVO, destZone, snapshotDataStoreVO.getSnapshotId());
return false;
}
}
return true;
} catch (ResourceAllocationException e) {
logger.error(String.format("Unable to allocate secondary storage resources for snapshot chain for %s with size: %d", snapshotVO, size), e);
return false;
}
return true;
}
@DB
private List<String> copySnapshotToZones(SnapshotVO snapshotVO, DataStore srcSecStore, List<DataCenterVO> dstZones) throws StorageUnavailableException, ResourceAllocationException {
AccountVO account = _accountDao.findById(snapshotVO.getAccountId());

View File

@ -34,10 +34,8 @@ import org.apache.cloudstack.annotation.AnnotationService;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd;
import org.apache.cloudstack.api.command.user.iso.GetUploadParamsForIsoCmd;
import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd;
import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd;
import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd;
import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.direct.download.DirectDownloadManager;
@ -197,19 +195,6 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
profile.setSize(templateSize);
}
profile.setUrl(url);
// Check that the resource limit for secondary storage won't be exceeded
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()),
ResourceType.secondary_storage,
UriUtils.getRemoteSize(url, followRedirects));
return profile;
}
@Override
public TemplateProfile prepare(GetUploadParamsForIsoCmd cmd) throws ResourceAllocationException {
TemplateProfile profile = super.prepare(cmd);
// Check that the resource limit for secondary storage won't be exceeded
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()), ResourceType.secondary_storage);
return profile;
}
@ -228,19 +213,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
profile.setForCks(cmd.isForCks());
}
profile.setUrl(url);
// Check that the resource limit for secondary storage won't be exceeded
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()),
ResourceType.secondary_storage,
UriUtils.getRemoteSize(url, followRedirects));
return profile;
}
@Override
public TemplateProfile prepare(GetUploadParamsForTemplateCmd cmd) throws ResourceAllocationException {
TemplateProfile profile = super.prepare(cmd);
// Check that the resource limit for secondary storage won't be exceeded
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()), ResourceType.secondary_storage);
return profile;
}
@ -268,7 +241,6 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
persistDirectDownloadTemplate(template.getId(), profile.getSize());
}
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
return template;
}
@ -377,7 +349,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
if(payloads.isEmpty()) {
throw new CloudRuntimeException("unable to find zone or an image store with enough capacity");
}
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
return payloads;
}
});
@ -396,13 +368,12 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
CreateTemplateContext<TemplateApiResult> context) {
TemplateApiResult result = callback.getResult();
TemplateInfo template = context.template;
VMTemplateVO tmplt = _tmpltDao.findById(template.getId());
if (result.isSuccess()) {
VMTemplateVO tmplt = _tmpltDao.findById(template.getId());
// need to grant permission for public templates
if (tmplt.isPublicTemplate()) {
_messageBus.publish(_name, TemplateManager.MESSAGE_REGISTER_PUBLIC_TEMPLATE_EVENT, PublishScope.LOCAL, tmplt.getId());
}
long accountId = tmplt.getAccountId();
if (template.getSize() != null) {
// publish usage event
String etype = EventTypes.EVENT_TEMPLATE_CREATE;
@ -431,9 +402,13 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
UsageEventUtils.publishUsageEvent(etype, template.getAccountId(), -1, template.getId(), template.getName(), null, null, physicalSize,
template.getSize(), VirtualMachineTemplate.class.getName(), template.getUuid());
}
_resourceLimitMgr.incrementResourceCount(accountId, ResourceType.secondary_storage, template.getSize());
}
}
if (tmplt != null) {
long accountId = tmplt.getAccountId();
Account account = _accountDao.findById(accountId);
_resourceLimitMgr.recalculateResourceCount(accountId, account.getDomainId(), ResourceType.secondary_storage.getOrdinal());
}
return null;
}

View File

@ -245,7 +245,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
Account account = _accountDao.findById(accountId);
Domain domain = _domainDao.findById(account.getDomainId());
payload.setDefaultMaxSecondaryStorageInGB(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null));
payload.setDefaultMaxSecondaryStorageInBytes(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null));
payload.setAccountId(accountId);
payload.setRemoteEndPoint(ep.getPublicAddr());
payload.setRequiresHvm(template.requiresHvm());
@ -364,8 +364,6 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
throw new IllegalArgumentException("Unable to find user with id " + userId);
}
_resourceLimitMgr.checkResourceLimit(templateOwner, ResourceType.template);
// If a zoneId is specified, make sure it is valid
if (zoneIdList != null) {
for (Long zoneId :zoneIdList) {

View File

@ -86,6 +86,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.messagebus.MessageBus;
import org.apache.cloudstack.framework.messagebus.PublishScope;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.cloudstack.secstorage.dao.SecondaryStorageHeuristicDao;
import org.apache.cloudstack.secstorage.heuristics.HeuristicType;
import org.apache.cloudstack.snapshot.SnapshotHelper;
@ -152,6 +153,7 @@ import com.cloud.hypervisor.HypervisorGuru;
import com.cloud.hypervisor.HypervisorGuruManager;
import com.cloud.projects.Project;
import com.cloud.projects.ProjectManager;
import com.cloud.resourcelimit.CheckedReservation;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.ImageStoreUploadMonitorImpl;
@ -201,6 +203,7 @@ import com.cloud.utils.EncryptionUtil;
import com.cloud.utils.EnumUtils;
import com.cloud.utils.Pair;
import com.cloud.utils.StringUtils;
import com.cloud.utils.UriUtils;
import com.cloud.utils.component.AdapterBase;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.concurrency.NamedThreadFactory;
@ -302,29 +305,27 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
private VMTemplateDetailsDao _tmpltDetailsDao;
@Inject
private HypervisorGuruManager _hvGuruMgr;
private List<TemplateAdapter> _adapters;
ExecutorService _preloadExecutor;
@Inject
ReservationDao reservationDao;
@Inject
private StorageCacheManager cacheMgr;
@Inject
private EndPointSelector selector;
@Inject
protected SnapshotHelper snapshotHelper;
@Inject
VnfTemplateManager vnfTemplateManager;
@Inject
TemplateDeployAsIsDetailsDao templateDeployAsIsDetailsDao;
@Inject
private SecondaryStorageHeuristicDao secondaryStorageHeuristicDao;
@Inject
private HeuristicRuleHelper heuristicRuleHelper;
private List<TemplateAdapter> _adapters;
ExecutorService _preloadExecutor;
protected boolean backupSnapshotAfterTakingSnapshot = SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value();
private TemplateAdapter getAdapter(HypervisorType type) {
@ -353,14 +354,30 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
@ActionEvent(eventType = EventTypes.EVENT_ISO_CREATE, eventDescription = "Creating ISO")
public VirtualMachineTemplate registerIso(RegisterIsoCmd cmd) throws ResourceAllocationException {
TemplateAdapter adapter = getAdapter(HypervisorType.None);
TemplateProfile profile = adapter.prepare(cmd);
VMTemplateVO template = adapter.create(profile);
Account owner = _accountMgr.getAccount(cmd.getEntityOwnerId());
if (template != null) {
CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid());
return template;
} else {
throw new CloudRuntimeException("Failed to create ISO");
// Secondary storage resource count is not incremented for BareMetalTemplateAdapter
// Note: checking the file size before registering will require the Management Server host to have access to the Internet and a DNS server
// If it does not, UriUtils.getRemoteSize will return 0L.
long secondaryStorageUsage = adapter instanceof HypervisorTemplateAdapter && !cmd.isDirectDownload() ?
UriUtils.getRemoteSize(cmd.getUrl(), StorageManager.DataStoreDownloadFollowRedirects.value()) : 0L;
try (CheckedReservation templateReservation = new CheckedReservation(owner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr);
CheckedReservation secondaryStorageReservation = new CheckedReservation(owner, ResourceType.secondary_storage, null, null, secondaryStorageUsage, reservationDao, _resourceLimitMgr)) {
TemplateProfile profile = adapter.prepare(cmd);
VMTemplateVO template = adapter.create(profile);
// Secondary storage resource usage will be incremented in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
if (secondaryStorageUsage > 0) {
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.secondary_storage, secondaryStorageUsage);
}
if (template != null) {
CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid());
return template;
} else {
throw new CloudRuntimeException("Failed to create ISO");
}
}
}
@ -380,17 +397,32 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
}
TemplateAdapter adapter = getAdapter(HypervisorType.getType(cmd.getHypervisor()));
TemplateProfile profile = adapter.prepare(cmd);
VMTemplateVO template = adapter.create(profile);
Account owner = _accountMgr.getAccount(cmd.getEntityOwnerId());
if (template != null) {
CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid());
if (cmd instanceof RegisterVnfTemplateCmd) {
vnfTemplateManager.persistVnfTemplate(template.getId(), (RegisterVnfTemplateCmd) cmd);
long secondaryStorageUsage = adapter instanceof HypervisorTemplateAdapter && !cmd.isDirectDownload() ?
UriUtils.getRemoteSize(cmd.getUrl(), StorageManager.DataStoreDownloadFollowRedirects.value()) : 0L;
try (CheckedReservation templateReservation = new CheckedReservation(owner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr);
CheckedReservation secondaryStorageReservation = new CheckedReservation(owner, ResourceType.secondary_storage, null, null, secondaryStorageUsage, reservationDao, _resourceLimitMgr)) {
TemplateProfile profile = adapter.prepare(cmd);
VMTemplateVO template = adapter.create(profile);
// Secondary storage resource usage will be recalculated in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack
// for HypervisorTemplateAdapter
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
if (secondaryStorageUsage > 0) {
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.secondary_storage, secondaryStorageUsage);
}
if (template != null) {
CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid());
if (cmd instanceof RegisterVnfTemplateCmd) {
vnfTemplateManager.persistVnfTemplate(template.getId(), (RegisterVnfTemplateCmd) cmd);
}
return template;
} else {
throw new CloudRuntimeException("Failed to create a Template");
}
return template;
} else {
throw new CloudRuntimeException("Failed to create a Template");
}
}
@ -455,17 +487,35 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
@Override
@ActionEvent(eventType = EventTypes.EVENT_ISO_CREATE, eventDescription = "Creating post upload ISO")
public GetUploadParamsResponse registerIsoForPostUpload(GetUploadParamsForIsoCmd cmd) throws ResourceAllocationException, MalformedURLException {
TemplateAdapter adapter = getAdapter(HypervisorType.None);
TemplateProfile profile = adapter.prepare(cmd);
return registerPostUploadInternal(adapter, profile);
Account owner = _accountMgr.getAccount(cmd.getEntityOwnerId());
try (CheckedReservation templateReservation = new CheckedReservation(owner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr)) {
TemplateAdapter adapter = getAdapter(HypervisorType.None);
TemplateProfile profile = adapter.prepare(cmd);
GetUploadParamsResponse response = registerPostUploadInternal(adapter, profile);
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
return response;
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "Creating post upload Template")
public GetUploadParamsResponse registerTemplateForPostUpload(GetUploadParamsForTemplateCmd cmd) throws ResourceAllocationException, MalformedURLException {
TemplateAdapter adapter = getAdapter(HypervisorType.getType(cmd.getHypervisor()));
TemplateProfile profile = adapter.prepare(cmd);
return registerPostUploadInternal(adapter, profile);
Account owner = _accountMgr.getAccount(cmd.getEntityOwnerId());
try (CheckedReservation templateReservation = new CheckedReservation(owner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr)) {
TemplateAdapter adapter = getAdapter(HypervisorType.getType(cmd.getHypervisor()));
TemplateProfile profile = adapter.prepare(cmd);
GetUploadParamsResponse response = registerPostUploadInternal(adapter, profile);
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
return response;
}
}
@Override
@ -527,7 +577,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
VMTemplateVO vmTemplate = _tmpltDao.findById(templateId);
if (vmTemplate == null) {
throw new InvalidParameterValueException("Unable to find Template id=" + templateId);
throw new InvalidParameterValueException("Unable to find Template ID=" + templateId);
}
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), AccessType.OperateEntry, true, vmTemplate);
@ -832,9 +882,6 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
// find the size of the template to be copied
TemplateDataStoreVO srcTmpltStore = _tmplStoreDao.findByStoreTemplate(srcSecStore.getId(), tmpltId);
_resourceLimitMgr.checkResourceLimit(account, ResourceType.template);
_resourceLimitMgr.checkResourceLimit(account, ResourceType.secondary_storage, new Long(srcTmpltStore.getSize()).longValue());
// Event details
String copyEventType;
if (template.getFormat().equals(ImageFormat.ISO)) {
@ -897,9 +944,6 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
}
}
}
return true;
} catch (Exception ex) {
logger.debug("Failed to copy Template to image store:{} ,will try next one", dstSecStore, ex);
}
@ -981,21 +1025,21 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
// sync template from cache store to region store if it is not there, for cases where we are going to migrate existing NFS to S3.
_tmpltSvr.syncTemplateToRegionStore(template, srcSecStore);
}
AccountVO templateOwner = _accountDao.findById(template.getAccountId());
for (Long destZoneId : destZoneIds) {
DataStore dstSecStore = getImageStore(destZoneId, templateId);
if (dstSecStore != null) {
logger.debug("There is Template {} in secondary storage {} in zone {} , don't need to copy", template, dstSecStore, dataCenterVOs.get(destZoneId));
continue;
}
if (!copy(userId, template, srcSecStore, dataCenterVOs.get(destZoneId))) {
failedZones.add(dataCenterVOs.get(destZoneId).getName());
}
else{
if (template.getSize() != null) {
// increase resource count
long accountId = template.getAccountId();
_resourceLimitMgr.incrementResourceCount(accountId, ResourceType.secondary_storage, template.getSize());
try (CheckedReservation secondaryStorageReservation = new CheckedReservation(templateOwner, ResourceType.secondary_storage, null, null, template.getSize(), reservationDao, _resourceLimitMgr)) {
if (!copy(userId, template, srcSecStore, dataCenterVOs.get(destZoneId))) {
failedZones.add(dataCenterVOs.get(destZoneId).getName());
continue;
}
_resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, template.getSize());
}
}
}
@ -1018,9 +1062,6 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
AccountVO account = _accountDao.findById(template.getAccountId());
_resourceLimitMgr.checkResourceLimit(account, ResourceType.template);
try {
_tmpltDao.addTemplateToZone(template, dstZoneId);
return true;
@ -1839,7 +1880,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
// decrement resource count
if (accountId != null) {
_resourceLimitMgr.decrementResourceCount(accountId, ResourceType.template);
_resourceLimitMgr.decrementResourceCount(accountId, ResourceType.secondary_storage, new Long(volumeFinal != null ? volumeFinal.getSize()
_resourceLimitMgr.decrementResourceCount(accountId, ResourceType.secondary_storage, (long)(volumeFinal != null ? volumeFinal.getSize()
: snapshotFinal.getSize()));
}
}
@ -1990,107 +2031,106 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
}
}
_resourceLimitMgr.checkResourceLimit(templateOwner, ResourceType.template);
_resourceLimitMgr.checkResourceLimit(templateOwner, ResourceType.secondary_storage, new Long(volume != null ? volume.getSize() : snapshot.getSize()).longValue());
long templateSize = volume != null ? volume.getSize() : snapshot.getSize();
try (CheckedReservation templateReservation = new CheckedReservation(templateOwner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr);
CheckedReservation secondaryStorageReservation = new CheckedReservation(templateOwner, ResourceType.secondary_storage, null, null, templateSize, reservationDao, _resourceLimitMgr)) {
if (!isAdmin || featured == null) {
featured = Boolean.FALSE;
}
Long guestOSId = cmd.getOsTypeId();
GuestOSVO guestOS = _guestOSDao.findById(guestOSId);
if (guestOS == null) {
throw new InvalidParameterValueException("GuestOS with ID: " + guestOSId + " does not exist.");
}
Long nextTemplateId = _tmpltDao.getNextInSequence(Long.class, "id");
String description = cmd.getDisplayText();
boolean isExtractable = false;
Long sourceTemplateId = null;
if (volume != null) {
VMTemplateVO template = ApiDBUtils.findTemplateById(volume.getTemplateId());
isExtractable = template != null && template.isExtractable() && template.getTemplateType() != Storage.TemplateType.SYSTEM;
if (template != null) {
arch = template.getArch();
if (!isAdmin || featured == null) {
featured = Boolean.FALSE;
}
if (volume.getIsoId() != null && volume.getIsoId() != 0) {
sourceTemplateId = volume.getIsoId();
} else if (volume.getTemplateId() != null) {
sourceTemplateId = volume.getTemplateId();
Long guestOSId = cmd.getOsTypeId();
GuestOSVO guestOS = _guestOSDao.findById(guestOSId);
if (guestOS == null) {
throw new InvalidParameterValueException("GuestOS with ID: " + guestOSId + " does not exist.");
}
}
String templateTag = cmd.getTemplateTag();
if (templateTag != null) {
if (logger.isDebugEnabled()) {
logger.debug("Adding Template tag: " + templateTag);
Long nextTemplateId = _tmpltDao.getNextInSequence(Long.class, "id");
String description = cmd.getDisplayText();
boolean isExtractable = false;
Long sourceTemplateId = null;
if (volume != null) {
VMTemplateVO template = ApiDBUtils.findTemplateById(volume.getTemplateId());
isExtractable = template != null && template.isExtractable() && template.getTemplateType() != Storage.TemplateType.SYSTEM;
if (template != null) {
arch = template.getArch();
}
if (volume.getIsoId() != null && volume.getIsoId() != 0) {
sourceTemplateId = volume.getIsoId();
} else if (volume.getTemplateId() != null) {
sourceTemplateId = volume.getTemplateId();
}
}
}
privateTemplate = new VMTemplateVO(nextTemplateId, name, ImageFormat.RAW, isPublic, featured, isExtractable,
TemplateType.USER, null, requiresHvmValue, bitsValue, templateOwner.getId(), null, description,
passwordEnabledValue, guestOS.getId(), true, hyperType, templateTag, cmd.getDetails(), sshKeyEnabledValue, isDynamicScalingEnabled, false, false, arch, null);
if (sourceTemplateId != null) {
if (logger.isDebugEnabled()) {
logger.debug("This Template is getting created from other Template, setting source Template ID to: " + sourceTemplateId);
String templateTag = cmd.getTemplateTag();
if (templateTag != null) {
if (logger.isDebugEnabled()) {
logger.debug("Adding Template tag: " + templateTag);
}
}
}
// for region wide storage, set cross zones flag
List<ImageStoreVO> stores = _imgStoreDao.findRegionImageStores();
if (!CollectionUtils.isEmpty(stores)) {
privateTemplate.setCrossZones(true);
}
privateTemplate.setSourceTemplateId(sourceTemplateId);
VMTemplateVO template = _tmpltDao.persist(privateTemplate);
// Increment the number of templates
if (template != null) {
Map<String, String> details = new HashMap<String, String>();
privateTemplate = new VMTemplateVO(nextTemplateId, name, ImageFormat.RAW, isPublic, featured, isExtractable,
TemplateType.USER, null, requiresHvmValue, bitsValue, templateOwner.getId(), null, description,
passwordEnabledValue, guestOS.getId(), true, hyperType, templateTag, cmd.getDetails(), sshKeyEnabledValue, isDynamicScalingEnabled, false, false, arch, null);
if (sourceTemplateId != null) {
VMTemplateVO sourceTemplate = _tmpltDao.findById(sourceTemplateId);
if (sourceTemplate != null && sourceTemplate.getDetails() != null) {
details.putAll(sourceTemplate.getDetails());
if (logger.isDebugEnabled()) {
logger.debug("This Template is getting created from other Template, setting source Template ID to: " + sourceTemplateId);
}
}
if (volume != null) {
Long vmId = volume.getInstanceId();
if (vmId != null) {
UserVmVO userVm = _userVmDao.findById(vmId);
if (userVm != null) {
_userVmDao.loadDetails(userVm);
Map<String, String> vmDetails = userVm.getDetails();
vmDetails = vmDetails.entrySet()
.stream()
.filter(map -> map.getValue() != null)
.collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
details.putAll(vmDetails);
// for region wide storage, set cross zones flag
List<ImageStoreVO> stores = _imgStoreDao.findRegionImageStores();
if (!CollectionUtils.isEmpty(stores)) {
privateTemplate.setCrossZones(true);
}
privateTemplate.setSourceTemplateId(sourceTemplateId);
VMTemplateVO template = _tmpltDao.persist(privateTemplate);
// Increment the number of templates
if (template != null) {
Map<String, String> details = new HashMap<String, String>();
if (sourceTemplateId != null) {
VMTemplateVO sourceTemplate = _tmpltDao.findById(sourceTemplateId);
if (sourceTemplate != null && sourceTemplate.getDetails() != null) {
details.putAll(sourceTemplate.getDetails());
}
}
}
if (cmd.getDetails() != null) {
details.remove(VmDetailConstants.ENCRYPTED_PASSWORD); // new password will be generated during vm deployment from password enabled template
details.putAll(cmd.getDetails());
}
if (!details.isEmpty()) {
privateTemplate.setDetails(details);
_tmpltDao.saveDetails(privateTemplate);
if (volume != null) {
Long vmId = volume.getInstanceId();
if (vmId != null) {
UserVmVO userVm = _userVmDao.findById(vmId);
if (userVm != null) {
_userVmDao.loadDetails(userVm);
Map<String, String> vmDetails = userVm.getDetails();
vmDetails = vmDetails.entrySet()
.stream()
.filter(map -> map.getValue() != null)
.collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));
details.putAll(vmDetails);
}
}
}
if (cmd.getDetails() != null) {
details.remove(VmDetailConstants.ENCRYPTED_PASSWORD); // new password will be generated during vm deployment from password enabled template
details.putAll(cmd.getDetails());
}
if (!details.isEmpty()) {
privateTemplate.setDetails(details);
_tmpltDao.saveDetails(privateTemplate);
}
_resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.template);
_resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, templateSize);
}
_resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.template);
_resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage,
new Long(volume != null ? volume.getSize() : snapshot.getSize()));
if (template != null) {
CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid());
return template;
} else {
throw new CloudRuntimeException("Failed to create a Template");
}
}
if (template != null) {
CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid());
return template;
} else {
throw new CloudRuntimeException("Failed to create a Template");
}
}
@Override
@ -2262,7 +2302,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
templateTag == null &&
forCks == null &&
arch == null &&
(!cleanupDetails && details == null) //update details in every case except this one
(! cleanupDetails && details == null) // update details in every case except this one
);
if (!updateNeeded) {
return template;
@ -2457,7 +2497,6 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
if (MapUtils.isEmpty(details)) {
return;
}
String bootMode = details.get(ApiConstants.BootType.UEFI.toString());
if (bootMode == null) {
return;

View File

@ -2214,6 +2214,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
checkIfAccountManagesProjects(accountId);
verifyCallerPrivilegeForUserOrAccountOperations(account);
validateNoDeleteProtectedVmsForAccount(account);
CallContext.current().putContextParameter(Account.class, account.getUuid());
@ -2253,6 +2254,23 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
return true;
}
private void validateNoDeleteProtectedVmsForAccount(Account account) {
long accountId = account.getId();
List<VMInstanceVO> deleteProtectedVms = _vmDao.listDeleteProtectedVmsByAccountId(accountId);
if (CollectionUtils.isEmpty(deleteProtectedVms)) {
return;
}
if (logger.isDebugEnabled()) {
List<String> vmUuids = deleteProtectedVms.stream().map(VMInstanceVO::getUuid).collect(Collectors.toList());
logger.debug("Cannot delete Account {}, delete protection enabled for Instances: {}", account, vmUuids);
}
throw new InvalidParameterValueException(
String.format("Cannot delete Account '%s'. One or more Instances have delete protection enabled.",
account.getAccountName()));
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_ENABLE, eventDescription = "enabling account", async = true)
public AccountVO enableAccount(String accountName, Long domainId, Long accountId) {

View File

@ -25,6 +25,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.inject.Inject;
@ -104,6 +105,7 @@ import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.ReservationContext;
import com.cloud.vm.ReservationContextImpl;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.dao.VMInstanceDao;
import org.apache.commons.lang3.StringUtils;
@ -364,6 +366,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom
}
_accountMgr.checkAccess(caller, domain);
// Check across the domain hierarchy (current + children) for any delete-protected instances
validateNoDeleteProtectedVmsForDomain(domain);
return deleteDomain(domain, cleanup);
}
@ -724,6 +728,22 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom
return success && deleteDomainSuccess;
}
private void validateNoDeleteProtectedVmsForDomain(Domain parentDomain) {
Set<Long> allDomainIds = getDomainChildrenIds(parentDomain.getPath());
List<VMInstanceVO> deleteProtectedVms = vmInstanceDao.listDeleteProtectedVmsByDomainIds(allDomainIds);
if (CollectionUtils.isEmpty(deleteProtectedVms)) {
return;
}
if (logger.isDebugEnabled()) {
List<String> vmUuids = deleteProtectedVms.stream().map(VMInstanceVO::getUuid).collect(Collectors.toList());
logger.debug("Cannot delete Domain {}, it has delete protection enabled for Instances: {}", parentDomain, vmUuids);
}
throw new InvalidParameterValueException(
String.format("Cannot delete Domain '%s'. One or more Instances have delete protection enabled.",
parentDomain.getName()));
}
@Override
public Pair<List<? extends Domain>, Integer> searchForDomains(ListDomainsCmd cmd) {
Account caller = getCaller();

View File

@ -60,9 +60,6 @@ import javax.naming.ConfigurationException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import com.cloud.serializer.GsonHelper;
import com.cloud.storage.SnapshotPolicyVO;
import com.cloud.storage.dao.SnapshotPolicyDao;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
@ -130,6 +127,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
import org.apache.cloudstack.extension.ExtensionHelper;
import org.apache.cloudstack.framework.async.AsyncCallFuture;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
@ -140,6 +138,7 @@ import org.apache.cloudstack.framework.messagebus.PublishScope;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.query.QueryService;
import org.apache.cloudstack.reservation.dao.ReservationDao;
import org.apache.cloudstack.resourcelimit.Reserver;
import org.apache.cloudstack.snapshot.SnapshotHelper;
import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.command.DettachCommand;
@ -268,12 +267,12 @@ import com.cloud.hypervisor.kvm.dpdk.DpdkHelper;
import com.cloud.kubernetes.cluster.KubernetesServiceHelper;
import com.cloud.network.IpAddressManager;
import com.cloud.network.Network;
import com.cloud.network.NetworkService;
import com.cloud.network.Network.GuestType;
import com.cloud.network.Network.IpAddresses;
import com.cloud.network.Network.Provider;
import com.cloud.network.Network.Service;
import com.cloud.network.NetworkModel;
import com.cloud.network.NetworkService;
import com.cloud.network.Networks.TrafficType;
import com.cloud.network.PhysicalNetwork;
import com.cloud.network.as.AutoScaleManager;
@ -315,6 +314,8 @@ import com.cloud.org.Grouping;
import com.cloud.resource.ResourceManager;
import com.cloud.resource.ResourceState;
import com.cloud.resourcelimit.CheckedReservation;
import com.cloud.resourcelimit.ReservationHelper;
import com.cloud.serializer.GsonHelper;
import com.cloud.server.ManagementService;
import com.cloud.server.ResourceTag;
import com.cloud.service.ServiceOfferingVO;
@ -326,6 +327,7 @@ import com.cloud.storage.GuestOSCategoryVO;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.ScopeType;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotPolicyVO;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.Storage;
import com.cloud.storage.Storage.ImageFormat;
@ -344,6 +346,7 @@ import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSCategoryDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.SnapshotPolicyDao;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VMTemplateZoneDao;
import com.cloud.storage.dao.VolumeDao;
@ -639,6 +642,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
@Inject
VnfTemplateManager vnfTemplateManager;
@Inject
ExtensionHelper extensionHelper;
private ScheduledExecutorService _executor = null;
private ScheduledExecutorService _vmIpFetchExecutor = null;
private int _expungeInterval;
@ -1374,9 +1380,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
Account owner = _accountMgr.getActiveAccountById(vmInstance.getAccountId());
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId());
List<Reserver> reservations = new ArrayList<>();
try {
if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) {
_resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), (long) currentCpu, (long) newCpu,
(long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template);
(long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template, reservations);
}
// Check that the specified service offering ID is valid
@ -1399,6 +1408,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
return _vmDao.findById(vmInstance.getId());
} finally {
ReservationHelper.closeAll(reservations);
}
}
/**
@ -2140,9 +2152,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId());
List<Reserver> reservations = new ArrayList<>();
try {
// Check resource limits
_resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), (long) currentCpu, (long) newCpu,
(long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template);
(long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template, reservations);
// Dynamically upgrade the running vms
boolean success = false;
@ -2214,6 +2228,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
}
}
return success;
} finally {
ReservationHelper.closeAll(reservations);
}
}
protected void validateDiskOfferingChecks(ServiceOfferingVO currentServiceOffering, ServiceOfferingVO newServiceOffering) {
@ -2399,10 +2417,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
List<Reserver> reservations = new ArrayList<>();
try {
// First check that the maximum number of UserVMs, CPU and Memory limit for the given
// accountId will not be exceeded
if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) {
resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), serviceOffering, template);
resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), serviceOffering, template, reservations);
}
_haMgr.cancelDestroy(vm, vm.getHostId());
@ -2427,6 +2447,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
//Update Resource Count for the given account
resourceCountIncrement(account.getId(), vm.isDisplayVm(), serviceOffering, template);
} finally {
ReservationHelper.closeAll(reservations);
}
}
});
@ -2861,53 +2885,25 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
long currentCpu = currentServiceOffering.getCpu();
long currentMemory = currentServiceOffering.getRamSize();
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId());
Long currentGpu = currentServiceOffering.getGpuCount() != null ? Long.valueOf(currentServiceOffering.getGpuCount()) : 0L;
Long newGpu = svcOffering.getGpuCount() != null ? Long.valueOf(svcOffering.getGpuCount()) : 0L;
List<Reserver> reservations = new ArrayList<>();
try {
checkVmLimits(owner, vmInstance, svcOffering, template, newCpu, currentCpu, newMemory, currentMemory, newGpu, currentGpu);
_resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), currentCpu, newCpu,
currentMemory, newMemory, currentServiceOffering, svcOffering, template, reservations);
if (newCpu > currentCpu) {
_resourceLimitMgr.incrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newCpu - currentCpu);
} else if (newCpu > 0 && currentCpu > newCpu){
_resourceLimitMgr.decrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentCpu - newCpu);
}
if (newMemory > currentMemory) {
_resourceLimitMgr.incrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newMemory - currentMemory);
} else if (newMemory > 0 && currentMemory > newMemory){
_resourceLimitMgr.decrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentMemory - newMemory);
}
} catch (ResourceAllocationException e) {
logger.error(String.format("Failed to updated VM due to: %s", e.getLocalizedMessage()));
throw new InvalidParameterValueException(e.getLocalizedMessage());
}
adjustVmLimits(owner, vmInstance, svcOffering, template, newCpu, currentCpu, newMemory, currentMemory, newGpu, currentGpu);
}
private void checkVmLimits(Account owner, UserVmVO vmInstance, ServiceOfferingVO svcOffering,
VMTemplateVO template, Long newCpu, Long currentCpu, Long newMemory, Long currentMemory,
Long newGpu, Long currentGpu
) throws ResourceAllocationException {
if (newCpu > currentCpu) {
_resourceLimitMgr.checkVmCpuResourceLimit(owner, vmInstance.isDisplay(), svcOffering,
template, newCpu - currentCpu);
}
if (newMemory > currentMemory) {
_resourceLimitMgr.checkVmMemoryResourceLimit(owner, vmInstance.isDisplay(), svcOffering,
template, newMemory - currentMemory);
}
if (newGpu > currentGpu) {
_resourceLimitMgr.checkVmGpuResourceLimit(owner, vmInstance.isDisplay(), svcOffering,
template, newGpu - currentGpu);
}
}
private void adjustVmLimits(Account owner, UserVmVO vmInstance, ServiceOfferingVO svcOffering,
VMTemplateVO template, Long newCpu, Long currentCpu, Long newMemory, Long currentMemory,
Long newGpu, Long currentGpu
) {
if (newCpu > currentCpu) {
_resourceLimitMgr.incrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newCpu - currentCpu);
} else if (newCpu > 0 && currentCpu > newCpu) {
_resourceLimitMgr.decrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentCpu - newCpu);
}
if (newMemory > currentMemory) {
_resourceLimitMgr.incrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newMemory - currentMemory);
} else if (newMemory > 0 && currentMemory > newMemory) {
_resourceLimitMgr.decrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentMemory - newMemory);
}
if (newGpu > currentGpu) {
_resourceLimitMgr.incrementVmGpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newGpu - currentGpu);
} else if (newGpu > 0 && currentGpu > newGpu) {
_resourceLimitMgr.decrementVmGpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentGpu - newGpu);
} finally {
ReservationHelper.closeAll(reservations);
}
}
@ -2973,6 +2969,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
.map(item -> (item).trim())
.collect(Collectors.toList());
userDenyListedSettings.addAll(QueryService.RootAdminOnlyVmSettings);
if (template != null && template.getExtensionId() != null) {
userDenyListedSettings.addAll(extensionHelper.getExtensionReservedResourceDetails(
template.getExtensionId()));
}
final List<String> userReadOnlySettings = Stream.of(QueryService.UserVMReadOnlyDetails.value().split(","))
.map(item -> (item).trim())
.collect(Collectors.toList());
@ -4427,7 +4428,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
throw new InvalidParameterValueException(String.format("Invalid disk offering %s specified for datadisk Template %s. Disk offering size should be greater than or equal to the Template size", dataDiskOffering, dataDiskTemplate));
}
_templateDao.loadDetails(dataDiskTemplate);
resourceLimitService.checkVolumeResourceLimit(owner, true, dataDiskOffering.getDiskSize(), dataDiskOffering);
}
}
@ -5777,13 +5777,135 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
return startVirtualMachine(vmId, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, true);
}
private Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachineUnchecked(UserVmVO vm, VMTemplateVO template, Long podId,
Long clusterId, Long hostId, @NotNull Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse,
boolean isExplicitHost, boolean isRootAdmin) throws ResourceUnavailableException, InsufficientCapacityException {
// check if vm is security group enabled
if (_securityGroupMgr.isVmSecurityGroupEnabled(vm.getId()) && _securityGroupMgr.getSecurityGroupsForVm(vm.getId()).isEmpty()
&& !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vm.getId()) && _networkModel.canAddDefaultSecurityGroup()) {
// if vm is not mapped to security group, create a mapping
if (logger.isDebugEnabled()) {
logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically");
}
SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId());
if (defaultSecurityGroup != null) {
List<Long> groupList = new ArrayList<>();
groupList.add(defaultSecurityGroup.getId());
_securityGroupMgr.addInstanceToGroups(vm, groupList);
}
}
// Choose deployment planner
// Host takes 1st preference, Cluster takes 2nd preference and Pod takes 3rd
// Default behaviour is invoked when host, cluster or pod are not specified
Pod destinationPod = getDestinationPod(podId, isRootAdmin);
Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin);
HostVO destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost);
DataCenterDeployment plan = null;
boolean deployOnGivenHost = false;
if (destinationHost != null) {
logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM");
_hostDao.loadHostTags(destinationHost);
validateStrictHostTagCheck(vm, destinationHost);
final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
Pair<Boolean, Boolean> cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false);
if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) {
String errorMsg;
if (!cpuCapabilityAndCapacity.first()) {
errorMsg = String.format("Cannot deploy the VM to specified host %s, requested CPU and speed is more than the host capability", destinationHost);
} else {
errorMsg = String.format("Cannot deploy the VM to specified host %s, host does not have enough free CPU or RAM, please check the logs", destinationHost);
}
logger.info(errorMsg);
if (!AllowDeployVmIfGivenHostFails.value()) {
throw new InvalidParameterValueException(errorMsg);
}
} else {
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null);
if (!AllowDeployVmIfGivenHostFails.value()) {
deployOnGivenHost = true;
}
}
} else if (destinationCluster != null) {
logger.debug("Destination Cluster to deploy the VM is specified, specifying a deployment plan to deploy the VM");
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationCluster.getPodId(), destinationCluster.getId(), null, null, null);
if (!AllowDeployVmIfGivenHostFails.value()) {
deployOnGivenHost = true;
}
} else if (destinationPod != null) {
logger.debug("Destination Pod to deploy the VM is specified, specifying a deployment plan to deploy the VM");
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationPod.getId(), null, null, null, null);
if (!AllowDeployVmIfGivenHostFails.value()) {
deployOnGivenHost = true;
}
}
// Set parameters
Map<VirtualMachineProfile.Param, Object> params = null;
if (vm.isUpdateParameters()) {
_vmDao.loadDetails(vm);
String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template);
if (!validPassword(password)) {
throw new InvalidParameterValueException("A valid password for this virtual machine was not provided.");
}
// Check if an SSH key pair was selected for the instance and if so
// use it to encrypt & save the vm password
encryptAndStorePassword(vm, password);
params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password);
}
if (additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) {
if (!HypervisorType.VMware.equals(vm.getHypervisorType())) {
throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType());
}
Object paramValue = additionalParams.get(VirtualMachineProfile.Param.BootIntoSetup);
if (logger.isTraceEnabled()) {
logger.trace("It was specified whether to enter setup mode: " + paramValue.toString());
}
params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.BootIntoSetup, paramValue);
}
VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid());
DeploymentPlanner planner = null;
if (deploymentPlannerToUse != null) {
// if set to null, the deployment planner would be later figured out either from global config var, or from
// the service offering
planner = _planningMgr.getDeploymentPlannerByName(deploymentPlannerToUse);
if (planner == null) {
throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse);
}
}
vmEntity.setParamsToEntity(additionalParams);
UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId());
String reservationId = vmEntity.reserve(planner, plan, new ExcludeList(), Long.toString(callerUser.getId()));
vmEntity.deploy(reservationId, Long.toString(callerUser.getId()), params, deployOnGivenHost);
Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> vmParamPair = new Pair(vm, params);
if (vm.isUpdateParameters()) {
// this value is not being sent to the backend; need only for api
// display purposes
if (template.isEnablePassword()) {
if (vm.getDetail(VmDetailConstants.PASSWORD) != null) {
vmInstanceDetailsDao.removeDetail(vm.getId(), VmDetailConstants.PASSWORD);
}
vm.setUpdateParameters(false);
_vmDao.update(vm.getId(), vm);
}
}
return vmParamPair;
}
@Override
public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId,
@NotNull Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse, boolean isExplicitHost)
throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException {
// Input validation
final Account callerAccount = CallContext.current().getCallingAccount();
UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId());
// if account is removed, return error
if (callerAccount == null || callerAccount.getRemoved() != null) {
@ -5811,133 +5933,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (owner.getState() == Account.State.DISABLED) {
throw new PermissionDeniedException(String.format("The owner of %s is disabled: %s", vm, owner));
}
boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId());
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) {
// check if account/domain is with in resource limits to start a new vm
ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
resourceLimitService.checkVmResourceLimit(owner, vm.isDisplayVm(), offering, template);
List<String> resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(offering, template);
try (CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, resourceLimitHostTags, 1l, reservationDao, resourceLimitService);
CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, resourceLimitHostTags, Long.valueOf(offering.getCpu()), reservationDao, resourceLimitService);
CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, resourceLimitHostTags, Long.valueOf(offering.getRamSize()), reservationDao, resourceLimitService);
) {
return startVirtualMachineUnchecked(vm, template, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, isExplicitHost, isRootAdmin);
}
} else {
return startVirtualMachineUnchecked(vm, template, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, isExplicitHost, isRootAdmin);
}
// check if vm is security group enabled
if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty()
&& !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) && _networkModel.canAddDefaultSecurityGroup()) {
// if vm is not mapped to security group, create a mapping
if (logger.isDebugEnabled()) {
logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically");
}
SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId());
if (defaultSecurityGroup != null) {
List<Long> groupList = new ArrayList<>();
groupList.add(defaultSecurityGroup.getId());
_securityGroupMgr.addInstanceToGroups(vm, groupList);
}
}
// Choose deployment planner
// Host takes 1st preference, Cluster takes 2nd preference and Pod takes 3rd
// Default behaviour is invoked when host, cluster or pod are not specified
boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId());
Pod destinationPod = getDestinationPod(podId, isRootAdmin);
Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin);
HostVO destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost);
DataCenterDeployment plan = null;
boolean deployOnGivenHost = false;
if (destinationHost != null) {
logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM");
_hostDao.loadHostTags(destinationHost);
validateStrictHostTagCheck(vm, destinationHost);
final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
Pair<Boolean, Boolean> cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false);
if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) {
String errorMsg;
if (!cpuCapabilityAndCapacity.first()) {
errorMsg = String.format("Cannot deploy the VM to specified host %s, requested CPU and speed is more than the host capability", destinationHost);
} else {
errorMsg = String.format("Cannot deploy the VM to specified host %s, host does not have enough free CPU or RAM, please check the logs", destinationHost);
}
logger.info(errorMsg);
if (!AllowDeployVmIfGivenHostFails.value()) {
throw new InvalidParameterValueException(errorMsg);
};
} else {
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null);
if (!AllowDeployVmIfGivenHostFails.value()) {
deployOnGivenHost = true;
}
}
} else if (destinationCluster != null) {
logger.debug("Destination Cluster to deploy the VM is specified, specifying a deployment plan to deploy the VM");
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationCluster.getPodId(), destinationCluster.getId(), null, null, null);
if (!AllowDeployVmIfGivenHostFails.value()) {
deployOnGivenHost = true;
}
} else if (destinationPod != null) {
logger.debug("Destination Pod to deploy the VM is specified, specifying a deployment plan to deploy the VM");
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationPod.getId(), null, null, null, null);
if (!AllowDeployVmIfGivenHostFails.value()) {
deployOnGivenHost = true;
}
}
// Set parameters
Map<VirtualMachineProfile.Param, Object> params = null;
if (vm.isUpdateParameters()) {
_vmDao.loadDetails(vm);
String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template);
if (!validPassword(password)) {
throw new InvalidParameterValueException("A valid password for this virtual machine was not provided.");
}
// Check if an SSH key pair was selected for the instance and if so
// use it to encrypt & save the vm password
encryptAndStorePassword(vm, password);
params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password);
}
if (additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) {
if (! HypervisorType.VMware.equals(vm.getHypervisorType())) {
throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType());
}
Object paramValue = additionalParams.get(VirtualMachineProfile.Param.BootIntoSetup);
if (logger.isTraceEnabled()) {
logger.trace("It was specified whether to enter setup mode: " + paramValue.toString());
}
params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.BootIntoSetup, paramValue);
}
VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid());
DeploymentPlanner planner = null;
if (deploymentPlannerToUse != null) {
// if set to null, the deployment planner would be later figured out either from global config var, or from
// the service offering
planner = _planningMgr.getDeploymentPlannerByName(deploymentPlannerToUse);
if (planner == null) {
throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse);
}
}
vmEntity.setParamsToEntity(additionalParams);
String reservationId = vmEntity.reserve(planner, plan, new ExcludeList(), Long.toString(callerUser.getId()));
vmEntity.deploy(reservationId, Long.toString(callerUser.getId()), params, deployOnGivenHost);
Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> vmParamPair = new Pair(vm, params);
if (vm != null && vm.isUpdateParameters()) {
// this value is not being sent to the backend; need only for api
// display purposes
if (template.isEnablePassword()) {
if (vm.getDetail(VmDetailConstants.PASSWORD) != null) {
vmInstanceDetailsDao.removeDetail(vm.getId(), VmDetailConstants.PASSWORD);
}
vm.setUpdateParameters(false);
_vmDao.update(vm.getId(), vm);
}
}
return vmParamPair;
}
/**
@ -7910,38 +7920,29 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
return findMigratedVm(vm.getId(), vm.getType());
}
protected void checkVolumesLimits(Account account, List<VolumeVO> volumes) throws ResourceAllocationException {
Long totalVolumes = 0L;
Long totalVolumesSize = 0L;
protected void checkVolumesLimits(Account account, List<VolumeVO> volumes, List<Reserver> reservations) throws ResourceAllocationException {
Map<Long, List<String>> diskOfferingTagsMap = new HashMap<>();
Map<String, Long> tagVolumeCountMap = new HashMap<>();
Map<String, Long> tagSizeMap = new HashMap<>();
for (VolumeVO volume : volumes) {
if (!volume.isDisplay()) {
continue;
}
totalVolumes++;
totalVolumesSize += volume.getSize();
if (!diskOfferingTagsMap.containsKey(volume.getDiskOfferingId())) {
diskOfferingTagsMap.put(volume.getDiskOfferingId(), _resourceLimitMgr.getResourceLimitStorageTags(
_diskOfferingDao.findById(volume.getDiskOfferingId())));
Long diskOfferingId = volume.getDiskOfferingId();
if (!diskOfferingTagsMap.containsKey(diskOfferingId)) {
DiskOffering diskOffering = _diskOfferingDao.findById(diskOfferingId);
List<String> tagsForDiskOffering = _resourceLimitMgr.getResourceLimitStorageTags(diskOffering);
diskOfferingTagsMap.put(diskOfferingId, tagsForDiskOffering);
}
List<String> tags = diskOfferingTagsMap.get(volume.getDiskOfferingId());
for (String tag : tags) {
if (tagVolumeCountMap.containsKey(tag)) {
tagVolumeCountMap.put(tag, tagVolumeCountMap.get(tag) + 1);
tagSizeMap.put(tag, tagSizeMap.get(tag) + volume.getSize());
} else {
tagVolumeCountMap.put(tag, 1L);
tagSizeMap.put(tag, volume.getSize());
}
}
}
_resourceLimitMgr.checkResourceLimit(account, ResourceType.volume, totalVolumes);
_resourceLimitMgr.checkResourceLimit(account, ResourceType.primary_storage, totalVolumesSize);
for (String tag : tagVolumeCountMap.keySet()) {
resourceLimitService.checkResourceLimitWithTag(account, ResourceType.volume, tag, tagVolumeCountMap.get(tag));
resourceLimitService.checkResourceLimitWithTag(account, ResourceType.primary_storage, tag, tagSizeMap.get(tag));
List<String> tags = diskOfferingTagsMap.get(diskOfferingId);
CheckedReservation volumeReservation = new CheckedReservation(account, ResourceType.volume, tags, 1L, reservationDao, resourceLimitService);
reservations.add(volumeReservation);
long size = ObjectUtils.defaultIfNull(volume.getSize(), 0L);
CheckedReservation primaryStorageReservation = new CheckedReservation(account, ResourceType.primary_storage, tags, size, reservationDao, resourceLimitService);
reservations.add(primaryStorageReservation);
}
}
@ -7983,14 +7984,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
final ServiceOfferingVO offering = serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId());
VirtualMachineTemplate template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
verifyResourceLimitsForAccountAndStorage(newAccount, vm, offering, volumes, template);
validateIfNewOwnerHasAccessToTemplate(vm, newAccount, template);
DomainVO domain = _domainDao.findById(domainId);
logger.trace("Verifying if the new account [{}] has access to the specified domain [{}].", newAccount, domain);
_accountMgr.checkAccess(newAccount, domain);
List<Reserver> reservations = new ArrayList<>();
try {
verifyResourceLimitsForAccountAndStorage(newAccount, vm, offering, volumes, template, reservations);
Network newNetwork = ensureDestinationNetwork(cmd, vm, newAccount);
try {
Transaction.execute(new TransactionCallbackNoReturn() {
@ -8007,6 +8010,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
throw e;
}
} finally {
ReservationHelper.closeAll(reservations);
}
logger.info("VM [{}] now belongs to account [{}].", vm.getInstanceName(), newAccountName);
return vm;
}
@ -8077,18 +8084,18 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
* @param volumes The volumes whose total size can exceed resource limits.
* @throws ResourceAllocationException
*/
protected void verifyResourceLimitsForAccountAndStorage(Account account, UserVmVO vm, ServiceOfferingVO offering, List<VolumeVO> volumes, VirtualMachineTemplate template)
protected void verifyResourceLimitsForAccountAndStorage(Account account, UserVmVO vm, ServiceOfferingVO offering, List<VolumeVO> volumes, VirtualMachineTemplate template, List<Reserver> reservations)
throws ResourceAllocationException {
logger.trace("Verifying if CPU and RAM for VM [{}] do not exceed account [{}] limit.", vm, account);
if (!countOnlyRunningVmsInResourceLimitation()) {
resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), offering, template);
resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), offering, template, reservations);
}
logger.trace("Verifying if volume size for VM [{}] does not exceed account [{}] limit.", vm, account);
checkVolumesLimits(account, volumes);
checkVolumesLimits(account, volumes, reservations);
}
protected boolean countOnlyRunningVmsInResourceLimitation() {
@ -8720,10 +8727,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
/**
* Attempts to create a network suitable for the creation of a VM ({@link NetworkOrchestrationService#createGuestNetwork}).
* If no physical network is found, throws a {@link InvalidParameterValueException}.
* @param caller The account which calls for the network creation.
* @param newAccount The account to which the network will be created.
* @param zone The zone where the network will be created.
* @param requiredOffering The network offering required to create the network.
* @return The NetworkVO for the network created.
* @throws InsufficientCapacityException
* @throws ResourceAllocationException
@ -8987,12 +8992,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
VMTemplateVO template = getRestoreVirtualMachineTemplate(caller, newTemplateId, rootVols, vm);
DiskOffering diskOffering = rootDiskOfferingId != null ? _diskOfferingDao.findById(rootDiskOfferingId) : null;
List<Reserver> reservations = new ArrayList<>();
try {
checkRestoreVmFromTemplate(vm, template, rootVols, diskOffering, details);
} catch (ResourceAllocationException e) {
logger.error("Failed to restore VM {} due to {}", vm, e.getMessage(), e);
throw new CloudRuntimeException("Failed to restore VM " + vm.getUuid() + " due to " + e.getMessage(), e);
}
checkRestoreVmFromTemplate(vm, template, rootVols, diskOffering, details, reservations);
if (needRestart) {
try {
@ -9139,6 +9142,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
logger.debug("Restore VM {} done successfully", vm);
return vm;
} catch (ResourceAllocationException e) {
logger.error("Failed to restore VM {} due to {}", vm, e.getMessage(), e);
throw new CloudRuntimeException("Failed to restore VM " + vm.getUuid() + " due to " + e.getMessage(), e);
} finally {
ReservationHelper.closeAll(reservations);
}
}
Long getRootVolumeSizeForVmRestore(Volume vol, VMTemplateVO template, UserVmVO userVm, DiskOffering diskOffering, Map<String, String> details, boolean update) {
@ -9238,7 +9247,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
* @param template template
* @throws InvalidParameterValueException if restore is not possible
*/
private void checkRestoreVmFromTemplate(UserVmVO vm, VMTemplateVO template, List<VolumeVO> rootVolumes, DiskOffering newDiskOffering, Map<String,String> details) throws ResourceAllocationException {
private void checkRestoreVmFromTemplate(UserVmVO vm, VMTemplateVO template, List<VolumeVO> rootVolumes, DiskOffering newDiskOffering, Map<String,String> details, List<Reserver> reservations) throws ResourceAllocationException {
TemplateDataStoreVO tmplStore;
if (!template.isDirectDownload()) {
tmplStore = _templateStoreDao.findByTemplateZoneReady(template.getId(), vm.getDataCenterId());
@ -9256,7 +9265,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (vm.getTemplateId() != template.getId()) {
ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
VMTemplateVO currentTemplate = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
_resourceLimitMgr.checkVmResourceLimitsForTemplateChange(owner, vm.isDisplay(), serviceOffering, currentTemplate, template);
_resourceLimitMgr.checkVmResourceLimitsForTemplateChange(owner, vm.isDisplay(), serviceOffering, currentTemplate, template, reservations);
}
for (Volume vol : rootVolumes) {
@ -9267,7 +9276,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (newDiskOffering != null || !vol.getSize().equals(newSize)) {
DiskOffering currentOffering = _diskOfferingDao.findById(vol.getDiskOfferingId());
_resourceLimitMgr.checkVolumeResourceLimitForDiskOfferingChange(owner, vol.isDisplay(),
vol.getSize(), newSize, currentOffering, newDiskOffering);
vol.getSize(), newSize, currentOffering, newDiskOffering, reservations);
}
}
}

View File

@ -449,7 +449,6 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
* Create, persist and return vm snapshot for userVmVo with given parameters.
* Persistence and support for custom service offerings are done on the same transaction
* @param userVmVo user vm
* @param vmId vm id
* @param vsDescription vm description
* @param vmSnapshotName vm snapshot name
* @param vsDisplayName vm snapshot display name
@ -816,37 +815,43 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
}
/**
* If snapshot was taken with a different service offering than actual used in vm, should change it back to it
* @param userVm vm to change service offering (if necessary)
* If snapshot was taken with a different service offering than actual used in vm, should change it back to it.
* We also call <code>changeUserVmServiceOffering</code> in case the service offering is dynamic in order to
* perform resource limit validation, as the amount of CPUs or memory may have been changed.
* @param vmSnapshotVo vm snapshot
*/
protected void updateUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) {
if (vmSnapshotVo.getServiceOfferingId() != userVm.getServiceOfferingId()) {
changeUserVmServiceOffering(userVm, vmSnapshotVo);
return;
}
ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(userVm.getServiceOfferingId());
if (serviceOffering.isDynamic()) {
changeUserVmServiceOffering(userVm, vmSnapshotVo);
}
}
/**
* Get user vm details as a map
* @param userVm user vm
* @param vmSnapshotVo snapshot to get the details from
* @return map
*/
protected Map<String, String> getVmMapDetails(UserVm userVm) {
List<VMInstanceDetailVO> userVmDetails = _vmInstanceDetailsDao.listDetails(userVm.getId());
protected Map<String, String> getVmMapDetails(VMSnapshotVO vmSnapshotVo) {
List<VMSnapshotDetailsVO> vmSnapshotDetails = _vmSnapshotDetailsDao.listDetails(vmSnapshotVo.getId());
Map<String, String> details = new HashMap<String, String>();
for (VMInstanceDetailVO detail : userVmDetails) {
for (VMSnapshotDetailsVO detail : vmSnapshotDetails) {
details.put(detail.getName(), detail.getValue());
}
return details;
}
/**
* Update service offering on {@link userVm} to the one specified in {@link vmSnapshotVo}
* Update service offering on {code}userVm{code} to the one specified in {code}vmSnapshotVo{code}
* @param userVm user vm to be updated
* @param vmSnapshotVo vm snapshot
*/
protected void changeUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) {
Map<String, String> vmDetails = getVmMapDetails(userVm);
Map<String, String> vmDetails = getVmMapDetails(vmSnapshotVo);
boolean result = upgradeUserVmServiceOffering(userVm, vmSnapshotVo.getServiceOfferingId(), vmDetails);
if (! result){
throw new CloudRuntimeException("Instance Snapshot reverting failed because the Instance service offering couldn't be changed to the one used when Snapshot was taken");
@ -855,8 +860,8 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
}
/**
* Upgrade virtual machine {@linkplain vmId} to new service offering {@linkplain serviceOfferingId}
* @param vmId vm id
* Upgrade virtual machine {code}vm{code} to new service offering {code}serviceOfferingId{code}
* @param vm vm
* @param serviceOfferingId service offering id
* @param details vm details
* @return if operation was successful
@ -1294,7 +1299,7 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
public Pair<JobInfo.Status, String> orchestrateCreateVMSnapshot(VmWorkCreateVMSnapshot work) throws Exception {
VMSnapshot snapshot = orchestrateCreateVMSnapshot(work.getVmId(), work.getVmSnapshotId(), work.isQuiesceVm());
return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED,
_jobMgr.marshallResultObject(new Long(snapshot.getId())));
_jobMgr.marshallResultObject(Long.valueOf(snapshot.getId())));
}
@ReflectionUse

Some files were not shown because too many files have changed in this diff Show More