This is an automated email from the ASF dual-hosted git repository.

sureshanaparti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 208ae84dd760aa7e18077ad063318df237fb23dc
Merge: 2c11171 da56a2a
Author: Suresh Kumar Anaparti <[email protected]>
AuthorDate: Tue Feb 8 19:01:34 2022 +0530

    Merge branch '4.16' into main

 .../java/com/cloud/storage/VolumeApiService.java   |   2 +
 .../java/org/apache/cloudstack/api/BaseCmd.java    |   2 +-
 .../service/VolumeOrchestrationService.java        |   7 +
 .../cloud/entity/api/VMEntityManagerImpl.java      |   2 +-
 .../engine/orchestration/VolumeOrchestrator.java   |  23 +--
 framework/managed-context/pom.xml                  |   6 +-
 .../apache/cloudstack/quota/QuotaManagerImpl.java  |  33 ++-
 .../cloudstack/quota/QuotaManagerImplTest.java     |   8 +-
 .../affinity/ExplicitDedicationProcessor.java      |   6 +
 plugins/alert-handlers/snmp-alerts/pom.xml         |   4 +-
 plugins/alert-handlers/syslog-alerts/pom.xml       |   4 +-
 .../cloud/deploy/ImplicitDedicationPlanner.java    |   2 +-
 plugins/hypervisors/ovm3/pom.xml                   |   4 +-
 plugins/integrations/kubernetes-service/pom.xml    |   6 +-
 plugins/network-elements/juniper-contrail/pom.xml  |   6 +
 plugins/user-authenticators/ldap/pom.xml           |   4 +
 pom.xml                                            |  20 +-
 .../main/java/com/cloud/configuration/Config.java  |  18 +-
 .../configuration/ConfigurationManagerImpl.java    |  18 +-
 .../deploy/DeploymentPlanningManagerImpl.java      |  19 +-
 .../com/cloud/storage/VolumeApiServiceImpl.java    |  20 +-
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |  33 +--
 .../java/com/cloud/vm/UserVmManagerImplTest.java   |  23 ++-
 services/console-proxy/server/pom.xml              |   4 +-
 services/secondary-storage/server/pom.xml          |   4 +-
 test/pom.xml                                       |   4 +-
 ui/public/locales/en.json                          |   3 +-
 .../view/InstanceNicsNetworkSelectListView.vue     | 149 ++++++++++++++
 ui/src/components/view/NicNetworkSelectForm.vue    | 228 +++++++++++++++++++++
 ui/src/views/compute/DeployVM.vue                  |  62 +++---
 ui/src/views/offering/AddComputeOffering.vue       |   2 +-
 utils/pom.xml                                      |   8 +-
 32 files changed, 583 insertions(+), 151 deletions(-)

diff --cc api/src/main/java/com/cloud/storage/VolumeApiService.java
index 516fe77,f7543b7..84559e6
--- a/api/src/main/java/com/cloud/storage/VolumeApiService.java
+++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java
@@@ -154,5 -153,5 +154,7 @@@ public interface VolumeApiService 
  
      Volume recoverVolume(long volumeId);
  
+     boolean validateVolumeSizeInBytes(long size);
++
 +    Volume changeDiskOfferingForVolume(ChangeOfferingForVolumeCmd cmd) throws 
ResourceAllocationException;
  }
diff --cc 
engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
index 3391532,9c1e998..a311912
--- 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@@ -83,9 -78,8 +78,9 @@@ import org.apache.cloudstack.storage.da
  import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
  import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
  import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
+ import org.apache.commons.collections.CollectionUtils;
  import org.apache.commons.collections.MapUtils;
 +import org.apache.commons.lang3.StringUtils;
- import org.apache.commons.collections.CollectionUtils;
  import org.apache.log4j.Logger;
  
  import com.cloud.agent.api.to.DataTO;
diff --cc 
server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 7d98dfb,e5347c4..0b4fcf8
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@@ -268,8 -269,9 +268,9 @@@ import com.cloud.vm.dao.VMInstanceDao
  import com.google.common.base.Enums;
  import com.google.common.base.MoreObjects;
  import com.google.common.base.Preconditions;
 -import com.google.common.base.Strings;
 +import org.apache.commons.lang3.StringUtils;
  import com.google.common.collect.Sets;
+ import com.googlecode.ipv6.IPv6Address;
  
  public class ConfigurationManagerImpl extends ManagerBase implements 
ConfigurationManager, ConfigurationService, Configurable {
      public static final Logger s_logger = 
Logger.getLogger(ConfigurationManagerImpl.class);
@@@ -3004,72 -3022,6 +3001,76 @@@
                  _serviceOfferingDetailsDao.saveDetails(detailsVO);
              }
  
 +            CallContext.current().setEventDetails("Service offering id=" + 
serviceOffering.getId());
 +            return serviceOffering;
 +        } else {
 +            return null;
 +        }
 +    }
 +
 +    private DiskOfferingVO createDiskOfferingInternal(final long userId, 
final boolean isSystem, final VirtualMachine.Type vmType,
 +                                                      final String name, 
final Integer cpu, final Integer ramSize, final Integer speed, final String 
displayText, final ProvisioningType typedProvisioningType, final boolean 
localStorageRequired,
 +                                                      final boolean offerHA, 
final boolean limitResourceUse, final boolean volatileVm, String tags, final 
List<Long> domainIds, List<Long> zoneIds, final String hostTag,
 +                                                      final Integer 
networkRate, final String deploymentPlanner, final Map<String, String> details, 
Long rootDiskSizeInGiB, final Boolean isCustomizedIops, Long minIops, Long 
maxIops,
 +                                                      Long bytesReadRate, 
Long bytesReadRateMax, Long bytesReadRateMaxLength,
 +                                                      Long bytesWriteRate, 
Long bytesWriteRateMax, Long bytesWriteRateMaxLength,
 +                                                      Long iopsReadRate, Long 
iopsReadRateMax, Long iopsReadRateMaxLength,
 +                                                      Long iopsWriteRate, 
Long iopsWriteRateMax, Long iopsWriteRateMaxLength,
 +                                                      final Integer 
hypervisorSnapshotReserve, String cacheMode, final Long storagePolicyID) {
 +
 +        DiskOfferingVO diskOffering = new DiskOfferingVO(name, displayText, 
typedProvisioningType, false, tags, false, localStorageRequired, false);
 +
 +        if (Boolean.TRUE.equals(isCustomizedIops) || isCustomizedIops == 
null) {
 +            minIops = null;
 +            maxIops = null;
 +        } else {
 +            if (minIops == null && maxIops == null) {
 +                minIops = 0L;
 +                maxIops = 0L;
 +            } else {
 +                if (minIops == null || minIops <= 0) {
 +                    throw new InvalidParameterValueException("The min IOPS 
must be greater than 0.");
 +                }
 +
 +                if (maxIops == null) {
 +                    maxIops = 0L;
 +                }
 +
 +                if (minIops > maxIops) {
 +                    throw new InvalidParameterValueException("The min IOPS 
must be less than or equal to the max IOPS.");
 +                }
 +            }
 +        }
 +
 +        if (rootDiskSizeInGiB != null && rootDiskSizeInGiB <= 0L) {
 +            throw new InvalidParameterValueException(String.format("The Root 
disk size is of %s GB but it must be greater than 0.", rootDiskSizeInGiB));
 +        } else if (rootDiskSizeInGiB != null) {
++            long maxVolumeSizeInGb = 
VolumeOrchestrationService.MaxVolumeSize.value();
++            if (rootDiskSizeInGiB > maxVolumeSizeInGb) {
++                throw new InvalidParameterValueException(String.format("The 
maximum size for a disk is %d GB.", maxVolumeSizeInGb));
++            }
 +            long rootDiskSizeInBytes = rootDiskSizeInGiB * GiB_TO_BYTES;
 +            diskOffering.setDiskSize(rootDiskSizeInBytes);
 +        }
 +
 +        diskOffering.setCustomizedIops(isCustomizedIops);
 +        diskOffering.setMinIops(minIops);
 +        diskOffering.setMaxIops(maxIops);
 +
 +        setBytesRate(diskOffering, bytesReadRate, bytesReadRateMax, 
bytesReadRateMaxLength, bytesWriteRate, bytesWriteRateMax, 
bytesWriteRateMaxLength);
 +        setIopsRate(diskOffering, iopsReadRate, iopsReadRateMax, 
iopsReadRateMaxLength, iopsWriteRate, iopsWriteRateMax, iopsWriteRateMaxLength);
 +
 +        if(cacheMode != null) {
 +            
diskOffering.setCacheMode(DiskOffering.DiskCacheMode.valueOf(cacheMode.toUpperCase()));
 +        }
 +
 +        if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 
0) {
 +            throw new InvalidParameterValueException("If provided, Hypervisor 
Snapshot Reserve must be greater than or equal to 0.");
 +        }
 +
 +        diskOffering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve);
 +
 +        if ((diskOffering = _diskOfferingDao.persist(diskOffering)) != null) {
              if (details != null && !details.isEmpty()) {
                  List<DiskOfferingDetailVO> diskDetailsVO = new 
ArrayList<DiskOfferingDetailVO>();
                  // Support disk offering details for below parameters
@@@ -3311,12 -3263,13 +3312,13 @@@
                                                  Long bytesWriteRate, Long 
bytesWriteRateMax, Long bytesWriteRateMaxLength,
                                                  Long iopsReadRate, Long 
iopsReadRateMax, Long iopsReadRateMaxLength,
                                                  Long iopsWriteRate, Long 
iopsWriteRateMax, Long iopsWriteRateMaxLength,
 -                                                final Integer 
hypervisorSnapshotReserve, String cacheMode, final Map<String, String> details, 
final Long storagePolicyID) {
 +                                                final Integer 
hypervisorSnapshotReserve, String cacheMode, final Map<String, String> details, 
final Long storagePolicyID, final boolean diskSizeStrictness) {
          long diskSize = 0;// special case for custom disk offerings
+         long maxVolumeSizeInGb = 
VolumeOrchestrationService.MaxVolumeSize.value();
          if (numGibibytes != null && numGibibytes <= 0) {
-             throw new InvalidParameterValueException("Please specify a disk 
size of at least 1 Gb.");
-         } else if (numGibibytes != null && numGibibytes > _maxVolumeSizeInGb) 
{
-             throw new InvalidParameterValueException("The maximum size for a 
disk is " + _maxVolumeSizeInGb + " Gb.");
+             throw new InvalidParameterValueException("Please specify a disk 
size of at least 1 GB.");
+         } else if (numGibibytes != null && numGibibytes > maxVolumeSizeInGb) {
+             throw new InvalidParameterValueException(String.format("The 
maximum size for a disk is %d GB.", maxVolumeSizeInGb));
          }
          final ProvisioningType typedProvisioningType = 
ProvisioningType.getProvisioningType(provisioningType);
  
diff --cc server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index ce7f35a,5912ff9..e42ea43
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@@ -322,10 -311,6 +322,9 @@@ public class VolumeApiServiceImpl exten
      public static final ConfigKey<Boolean> AllowUserExpungeRecoverVolume = 
new ConfigKey<Boolean>("Advanced", Boolean.class, 
"allow.user.expunge.recover.volume", "true",
              "Determines whether users can expunge or recover their volume", 
true, ConfigKey.Scope.Account);
  
 +    public static final ConfigKey<Boolean> 
MatchStoragePoolTagsWithDiskOffering = new ConfigKey<Boolean>("Advanced", 
Boolean.class, "match.storage.pool.tags.with.disk.offering", "true",
 +            "If true, volume's disk offering can be changed only with the 
matched storage tags", true, ConfigKey.Scope.Zone);
 +
-     private long _maxVolumeSizeInGb;
      private final StateMachine2<Volume.State, Volume.Event, Volume> 
_volStateMachine;
  
      private static final Set<Volume.State> STATES_VOLUME_CANNOT_BE_DESTROYED 
= new HashSet<>(Arrays.asList(Volume.State.Destroy, Volume.State.Expunging, 
Volume.State.Expunged, Volume.State.Allocated));
@@@ -1668,349 -1634,6 +1668,349 @@@
      }
  
      @Override
 +    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CHANGE_DISK_OFFERING, 
eventDescription = "Changing disk offering of a volume")
 +    public Volume changeDiskOfferingForVolume(ChangeOfferingForVolumeCmd cmd) 
throws ResourceAllocationException {
 +        Long newSize = cmd.getSize();
 +        Long newMinIops = cmd.getMinIops();
 +        Long newMaxIops = cmd.getMaxIops();
 +        Long newDiskOfferingId = cmd.getNewDiskOfferingId();
 +        boolean shrinkOk = cmd.isShrinkOk();
 +        boolean autoMigrateVolume = cmd.getAutoMigrate();
 +
 +        VolumeVO volume = _volsDao.findById(cmd.getId());
 +        if (volume == null) {
 +            throw new InvalidParameterValueException("No such volume");
 +        }
 +
 +        /* Does the caller have authority to act on this volume? */
 +        _accountMgr.checkAccess(CallContext.current().getCallingAccount(), 
null, true, volume);
 +
 +        return changeDiskOfferingForVolumeInternal(volume, newDiskOfferingId, 
newSize, newMinIops, newMaxIops, autoMigrateVolume, shrinkOk);
 +    }
 +
 +    private Volume changeDiskOfferingForVolumeInternal(VolumeVO volume, Long 
newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean 
autoMigrateVolume, boolean shrinkOk) throws ResourceAllocationException {
 +        DiskOfferingVO existingDiskOffering = 
_diskOfferingDao.findById(volume.getDiskOfferingId());
 +        DiskOfferingVO newDiskOffering = 
_diskOfferingDao.findById(newDiskOfferingId);
 +        Integer newHypervisorSnapshotReserve = null;
 +
 +        boolean volumeMigrateRequired = false;
 +        boolean volumeResizeRequired = false;
 +
 +        // VALIDATIONS
 +        Long updateNewSize[] = {newSize};
 +        Long updateNewMinIops[] = {newMinIops};
 +        Long updateNewMaxIops[] = {newMaxIops};
 +        Integer updateNewHypervisorSnapshotReserve[] = 
{newHypervisorSnapshotReserve};
 +        validateVolumeResizeWithNewDiskOfferingAndLoad(volume, 
existingDiskOffering, newDiskOffering, updateNewSize, updateNewMinIops, 
updateNewMaxIops, updateNewHypervisorSnapshotReserve);
 +        newSize = updateNewSize[0];
 +        newMinIops = updateNewMinIops[0];
 +        newMaxIops = updateNewMaxIops[0];
 +        newHypervisorSnapshotReserve = updateNewHypervisorSnapshotReserve[0];
 +        long currentSize = volume.getSize();
 +        validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk);
 +
 +        /* If this volume has never been beyond allocated state, short 
circuit everything and simply update the database. */
 +        // We need to publish this event to usage_volume table
 +        if (volume.getState() == Volume.State.Allocated) {
 +            s_logger.debug(String.format("Volume %s is in the allocated 
state, but has never been created. Simply updating database with new size and 
IOPS.", volume.getUuid()));
 +
 +            volume.setSize(newSize);
 +            volume.setMinIops(newMinIops);
 +            volume.setMaxIops(newMaxIops);
 +            volume.setHypervisorSnapshotReserve(newHypervisorSnapshotReserve);
 +
 +            if (newDiskOffering != null) {
 +                volume.setDiskOfferingId(newDiskOfferingId);
 +            }
 +
 +            _volsDao.update(volume.getId(), volume);
 +            if (currentSize != newSize) {
 +                
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_RESIZE, 
volume.getAccountId(), volume.getDataCenterId(), volume.getId(), 
volume.getName(),
 +                        volume.getDiskOfferingId(), volume.getTemplateId(), 
volume.getSize(), Volume.class.getName(), volume.getUuid());
 +            }
 +            return volume;
 +        }
 +
 +        if (currentSize != newSize || newMaxIops != volume.getMaxIops() || 
newMinIops != volume.getMinIops()) {
 +            volumeResizeRequired = true;
 +            validateVolumeReadyStateAndHypervisorChecks(volume, currentSize, 
newSize);
 +        }
 +
 +        StoragePoolVO existingStoragePool = 
_storagePoolDao.findById(volume.getPoolId());
 +
 +        Pair<List<? extends StoragePool>, List<? extends StoragePool>> 
poolsPair = 
managementService.listStoragePoolsForMigrationOfVolumeInternal(volume.getId(), 
newDiskOffering.getId(), newSize, newMinIops, newMaxIops, true);
 +        List<? extends StoragePool> suitableStoragePools = poolsPair.second();
 +
 +        if (!suitableStoragePools.stream().anyMatch(p -> (p.getId() == 
existingStoragePool.getId()))) {
 +            volumeMigrateRequired = true;
 +            if (!autoMigrateVolume) {
 +                throw new 
InvalidParameterValueException(String.format("Failed to change offering for 
volume %s since automigrate is set to false but volume needs to migrated", 
volume.getUuid()));
 +            }
 +        }
 +
 +        if (!volumeMigrateRequired && !volumeResizeRequired) {
 +            _volsDao.updateDiskOffering(volume.getId(), 
newDiskOffering.getId());
 +            volume = _volsDao.findById(volume.getId());
 +            return volume;
 +        }
 +
 +        if (volumeMigrateRequired) {
 +            if (CollectionUtils.isEmpty(poolsPair.first()) && 
CollectionUtils.isEmpty(poolsPair.second())) {
 +                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, 
String.format("Volume change offering operation failed for volume ID: %s as no 
suitable pool(s) found for migrating to support new disk offering", 
volume.getUuid()));
 +            }
 +            Collections.shuffle(suitableStoragePools);
 +            MigrateVolumeCmd migrateVolumeCmd = new 
MigrateVolumeCmd(volume.getId(), suitableStoragePools.get(0).getId(), 
newDiskOffering.getId(), true);
 +            try {
 +                volume = (VolumeVO) migrateVolume(migrateVolumeCmd);
 +                if (volume == null) {
 +                    throw new CloudRuntimeException(String.format("Volume 
change offering operation failed for volume ID: %s migration failed to storage 
pool %s", volume.getUuid(), suitableStoragePools.get(0).getId()));
 +                }
 +            } catch (Exception e) {
 +                throw new CloudRuntimeException(String.format("Volume change 
offering operation failed for volume ID: %s migration failed to storage pool %s 
due to %s", volume.getUuid(), suitableStoragePools.get(0).getId(), 
e.getMessage()));
 +            }
 +        }
 +
 +        if (volumeResizeRequired) {
 +            // refresh volume data
 +            volume = _volsDao.findById(volume.getId());
 +            try {
 +                volume = resizeVolumeInternal(volume, newDiskOffering, 
currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, 
shrinkOk);
 +            } catch (Exception e) {
 +                if (volumeMigrateRequired) {
 +                    s_logger.warn(String.format("Volume change offering 
operation succeeded for volume ID: %s but volume resize operation failed, so 
please try resize volume operation separately", volume.getUuid()));
 +                } else {
 +                    throw new CloudRuntimeException(String.format("Volume 
change offering operation failed for volume ID: %s due to resize volume 
operation failed", volume.getUuid()));
 +                }
 +            }
 +        }
 +
 +        return volume;
 +    }
 +
 +    private VolumeVO resizeVolumeInternal(VolumeVO volume, DiskOfferingVO 
newDiskOffering, Long currentSize, Long newSize, Long newMinIops, Long 
newMaxIops, Integer newHypervisorSnapshotReserve, boolean shrinkOk) throws 
ResourceAllocationException {
 +        UserVmVO userVm = _userVmDao.findById(volume.getInstanceId());
 +        HypervisorType hypervisorType = 
_volsDao.getHypervisorType(volume.getId());
 +
 +        if (userVm != null) {
 +            if (volume.getVolumeType().equals(Volume.Type.ROOT) && 
userVm.getPowerState() != VirtualMachine.PowerState.PowerOff && hypervisorType 
== HypervisorType.VMware) {
 +                s_logger.error(" For ROOT volume resize VM should be in Power 
Off state.");
 +                throw new InvalidParameterValueException("VM current state is 
: " + userVm.getPowerState() + ". But VM should be in " + 
VirtualMachine.PowerState.PowerOff + " state.");
 +            }
 +            // serialize VM operation
 +            AsyncJobExecutionContext jobContext = 
AsyncJobExecutionContext.getCurrentExecutionContext();
 +
 +            if 
(jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
 +                // avoid re-entrance
 +
 +                VmWorkJobVO placeHolder = null;
 +
 +                placeHolder = createPlaceHolderWork(userVm.getId());
 +
 +                try {
 +                    return orchestrateResizeVolume(volume.getId(), 
currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve,
 +                            newDiskOffering != null ? newDiskOffering.getId() 
: null, shrinkOk);
 +                } finally {
 +                    _workJobDao.expunge(placeHolder.getId());
 +                }
 +            } else {
 +                Outcome<Volume> outcome = 
resizeVolumeThroughJobQueue(userVm.getId(), volume.getId(), currentSize, 
newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve,
 +                        newDiskOffering != null ? newDiskOffering.getId() : 
null, shrinkOk);
 +
 +                try {
 +                    outcome.get();
 +                } catch (InterruptedException e) {
 +                    throw new RuntimeException("Operation was interrupted", 
e);
 +                } catch (java.util.concurrent.ExecutionException e) {
 +                    throw new RuntimeException("Execution exception", e);
 +                }
 +
 +                Object jobResult = 
_jobMgr.unmarshallResultObject(outcome.getJob());
 +
 +                if (jobResult != null) {
 +                    if (jobResult instanceof ConcurrentOperationException) {
 +                        throw (ConcurrentOperationException)jobResult;
 +                    } else if (jobResult instanceof 
ResourceAllocationException) {
 +                        throw (ResourceAllocationException)jobResult;
 +                    } else if (jobResult instanceof RuntimeException) {
 +                        throw (RuntimeException)jobResult;
 +                    } else if (jobResult instanceof Throwable) {
 +                        throw new RuntimeException("Unexpected exception", 
(Throwable)jobResult);
 +                    } else if (jobResult instanceof Long) {
 +                        return _volsDao.findById((Long)jobResult);
 +                    }
 +                }
 +
 +                return volume;
 +            }
 +        }
 +
 +        return orchestrateResizeVolume(volume.getId(), currentSize, newSize, 
newMinIops, newMaxIops, newHypervisorSnapshotReserve, newDiskOffering != null ? 
newDiskOffering.getId() : null,
 +                shrinkOk);
 +    }
 +
 +    private void validateVolumeReadyStateAndHypervisorChecks(VolumeVO volume, 
long currentSize, Long newSize) {
 +        // checking if there are any ongoing snapshots on the volume which is 
to be resized
 +        List<SnapshotVO> ongoingSnapshots = 
_snapshotDao.listByStatus(volume.getId(), Snapshot.State.Creating, 
Snapshot.State.CreatedOnPrimary, Snapshot.State.BackingUp);
 +        if (ongoingSnapshots.size() > 0) {
 +            throw new CloudRuntimeException("There is/are unbacked up 
snapshot(s) on this volume, resize volume is not permitted, please try again 
later.");
 +        }
 +
 +        /* Only works for KVM/XenServer/VMware (or "Any") for now, and 
volumes with 'None' since they're just allocated in DB */
 +        HypervisorType hypervisorType = 
_volsDao.getHypervisorType(volume.getId());
 +
 +        if (hypervisorType != HypervisorType.KVM && hypervisorType != 
HypervisorType.XenServer
 +                && hypervisorType != HypervisorType.VMware && hypervisorType 
!= HypervisorType.Any
 +                && hypervisorType != HypervisorType.None) {
 +            throw new InvalidParameterValueException("Hypervisor " + 
hypervisorType + " does not support volume resize");
 +        }
 +
 +        if (volume.getState() != Volume.State.Ready && volume.getState() != 
Volume.State.Allocated) {
 +            throw new InvalidParameterValueException("Volume should be in 
ready or allocated state before attempting a resize. Volume " + 
volume.getUuid() + " is in state " + volume.getState() + ".");
 +        }
 +
 +        if (hypervisorType.equals(HypervisorType.VMware) && newSize < 
currentSize) {
 +            throw new InvalidParameterValueException("VMware doesn't support 
shrinking volume from larger size: " + currentSize + " GB to a smaller size: " 
+ newSize + " GB");
 +        }
 +
 +        UserVmVO userVm = _userVmDao.findById(volume.getInstanceId());
 +        if (userVm != null) {
 +            if (volume.getVolumeType().equals(Volume.Type.ROOT) && 
userVm.getPowerState() != VirtualMachine.PowerState.PowerOff && hypervisorType 
== HypervisorType.VMware) {
 +                s_logger.error(" For ROOT volume resize VM should be in Power 
Off state.");
 +                throw new InvalidParameterValueException("VM current state is 
: " + userVm.getPowerState() + ". But VM should be in " + 
VirtualMachine.PowerState.PowerOff + " state.");
 +            }
 +        }
 +    }
 +
 +    private void validateVolumeResizeWithNewDiskOfferingAndLoad(VolumeVO 
volume, DiskOfferingVO existingDiskOffering, DiskOfferingVO newDiskOffering, 
Long[] newSize, Long[] newMinIops, Long[] newMaxIops, Integer[] 
newHypervisorSnapshotReserve) {
 +        if (newDiskOffering.getRemoved() != null) {
 +            throw new InvalidParameterValueException("Requested disk offering 
has been removed.");
 +        }
 +
 +        if (newDiskOffering.getId() == existingDiskOffering.getId()) {
 +            throw new InvalidParameterValueException(String.format("Volume %s 
already have the new disk offering %s provided", volume.getUuid(), 
existingDiskOffering.getUuid()));
 +        }
 +
 +        if (existingDiskOffering.getDiskSizeStrictness() != 
newDiskOffering.getDiskSizeStrictness()) {
 +            throw new InvalidParameterValueException("Disk offering size 
strictness does not match with new disk offering");
 +        }
 +
 +        if 
(MatchStoragePoolTagsWithDiskOffering.valueIn(volume.getDataCenterId())) {
 +            if 
(!doesNewDiskOfferingHasTagsAsOldDiskOffering(existingDiskOffering, 
newDiskOffering)) {
 +                throw new 
InvalidParameterValueException(String.format("Selected disk offering %s does 
not have tags as in existing disk offering of volume %s", 
existingDiskOffering.getUuid(), volume.getUuid()));
 +            }
 +        }
 +
 +        Long instanceId = volume.getInstanceId();
 +        VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(instanceId);
 +        if (volume.getVolumeType().equals(Volume.Type.ROOT)) {
 +            ServiceOfferingVO serviceOffering = 
_serviceOfferingDao.findById(vmInstanceVO.getServiceOfferingId());
 +            if (serviceOffering != null && 
serviceOffering.getDiskOfferingStrictness()) {
 +                throw new 
InvalidParameterValueException(String.format("Cannot resize ROOT volume [%s] 
with new disk offering since existing disk offering is strictly assigned to the 
ROOT volume.", volume.getName()));
 +            }
 +        }
 +
 +        
_configMgr.checkDiskOfferingAccess(_accountMgr.getActiveAccountById(volume.getAccountId()),
 newDiskOffering, _dcDao.findById(volume.getDataCenterId()));
 +
 +        if (newDiskOffering.getDiskSize() > 0 && 
!newDiskOffering.isComputeOnly()) {
 +            newSize[0] = (Long) newDiskOffering.getDiskSize();
 +        } else if (newDiskOffering.isCustomized()) {
 +            if (newSize[0] == null) {
 +                throw new InvalidParameterValueException("The new disk 
offering requires that a size be specified.");
 +            }
 +
 +            // convert from GiB to bytes
 +            newSize[0] = newSize[0] << 30;
 +        } else {
 +            if (newSize[0] != null) {
 +                throw new InvalidParameterValueException("You cannot pass in 
a custom disk size to a non-custom disk offering.");
 +            }
 +
 +            if (newDiskOffering.isComputeOnly() && 
newDiskOffering.getDiskSize() == 0) {
 +                newSize[0] = volume.getSize();
 +            } else {
 +                newSize[0] = newDiskOffering.getDiskSize();
 +            }
 +            if (newDiskOffering.isCustomizedIops() != null && 
newDiskOffering.isCustomizedIops()) {
 +                newMinIops[0] = newMinIops[0] != null ? newMinIops[0] : 
volume.getMinIops();
 +                newMaxIops[0] = newMaxIops[0] != null ? newMaxIops[0] : 
volume.getMaxIops();
 +
 +                validateIops(newMinIops[0], newMaxIops[0], 
volume.getPoolType());
 +            } else {
 +                newMinIops[0] = newDiskOffering.getMinIops();
 +                newMaxIops[0] = newDiskOffering.getMaxIops();
 +            }
 +
 +            // if the hypervisor snapshot reserve value is null, it must 
remain null (currently only KVM uses null and null is all KVM uses for a value 
here)
 +            newHypervisorSnapshotReserve[0] = 
volume.getHypervisorSnapshotReserve() != null ? 
newDiskOffering.getHypervisorSnapshotReserve() : null;
 +        }
 +
 +        if (existingDiskOffering.getDiskSizeStrictness() && 
!(volume.getSize().equals(newSize[0]))) {
 +            throw new InvalidParameterValueException(String.format("Resize 
volume for %s is not allowed since disk offering's size is fixed", 
volume.getName()));
 +        }
 +        checkIfVolumeIsRootAndVmIsRunning(newSize[0], volume, vmInstanceVO);
 +
 +    }
 +
 +    private void validateVolumeResizeWithSize(VolumeVO volume, long 
currentSize, Long newSize, boolean shrinkOk) throws ResourceAllocationException 
{
 +
 +        // if the caller is looking to change the size of the volume
 +        if (currentSize != newSize) {
 +            if (volume.getInstanceId() != null) {
 +                // Check that VM to which this volume is attached does not 
have VM snapshots
 +                if (_vmSnapshotDao.findByVm(volume.getInstanceId()).size() > 
0) {
 +                    throw new InvalidParameterValueException("A volume that 
is attached to a VM with any VM snapshots cannot be resized.");
 +                }
 +            }
 +
-             if (!validateVolumeSizeRange(newSize)) {
++            if (!validateVolumeSizeInBytes(newSize)) {
 +                throw new InvalidParameterValueException("Requested size out 
of range");
 +            }
 +
 +            Long storagePoolId = volume.getPoolId();
 +
 +            if (storagePoolId != null) {
 +                StoragePoolVO storagePoolVO = 
_storagePoolDao.findById(storagePoolId);
 +
 +                if (storagePoolVO.isManaged()) {
 +                    Long instanceId = volume.getInstanceId();
 +
 +                    if (instanceId != null) {
 +                        VMInstanceVO vmInstanceVO = 
_vmInstanceDao.findById(instanceId);
 +
 +                        if (vmInstanceVO.getHypervisorType() == 
HypervisorType.KVM && vmInstanceVO.getState() != State.Stopped) {
 +                            throw new CloudRuntimeException("This kind of KVM 
disk cannot be resized while it is connected to a VM that's not in the Stopped 
state.");
 +                        }
 +                    }
 +                }
 +            }
 +
 +            /*
 +             * Let's make certain they (think they) know what they're doing 
if they
 +             * want to shrink by forcing them to provide the shrinkok 
parameter.
 +             * This will be checked again at the hypervisor level where we 
can see
 +             * the actual disk size.
 +             */
 +            if (currentSize > newSize) {
 +                if (volume != null && 
ImageFormat.QCOW2.equals(volume.getFormat()) && 
!Volume.State.Allocated.equals(volume.getState())) {
 +                    String message = "Unable to shrink volumes of type QCOW2";
 +                    s_logger.warn(message);
 +                    throw new InvalidParameterValueException(message);
 +                }
 +            }
 +            if (currentSize > newSize && !shrinkOk) {
 +                throw new InvalidParameterValueException("Going from existing 
size of " + currentSize + " to size of " + newSize + " would shrink the volume."
 +                        + "Need to sign off by supplying the shrinkok 
parameter with value of true.");
 +            }
 +
 +            if (newSize > currentSize) {
 +                /* Check resource limit for this account on primary storage 
resource */
 +                
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()),
 ResourceType.primary_storage, volume.isDisplayVolume(),
 +                        new Long(newSize - currentSize).longValue());
 +            }
 +        }
 +    }
 +
 +    @Override
      @ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription 
= "attaching volume", async = true)
      public Volume attachVolumeToVM(AttachVolumeCmd command) {
          return attachVolumeToVM(command.getVirtualMachineId(), 
command.getId(), command.getDeviceId());
diff --cc server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 275f701,58ea4e9..d553ab4
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@@ -3802,28 -3830,30 +3801,28 @@@ public class UserVmManagerImpl extends 
          // check if account/domain is with in resource limits to create a new 
vm
          boolean isIso = Storage.ImageFormat.ISO == template.getFormat();
  
 -        long volumesSize = configureCustomRootDiskSize(customParameters, 
template, hypervisorType, offering);
 -
 -        if (diskOfferingId != null) {
 -            long size = 0;
 -            DiskOfferingVO diskOffering = 
_diskOfferingDao.findById(diskOfferingId);
 -            if (diskOffering == null) {
 -                throw new InvalidParameterValueException("Specified disk 
offering cannot be found");
 -            }
 -            if (diskOffering.isCustomized()) {
 -                if (diskSize == null) {
 -                    throw new InvalidParameterValueException("This disk 
offering requires a custom size specified");
 -                }
 -                Long customDiskOfferingMaxSize = 
VolumeOrchestrationService.CustomDiskOfferingMaxSize.value();
 -                Long customDiskOfferingMinSize = 
VolumeOrchestrationService.CustomDiskOfferingMinSize.value();
 -                if ((diskSize < customDiskOfferingMinSize) || (diskSize > 
customDiskOfferingMaxSize)) {
 -                    throw new InvalidParameterValueException("VM Creation 
failed. Volume size: " + diskSize + "GB is out of allowed range. Max: " + 
customDiskOfferingMaxSize
 -                            + " Min:" + customDiskOfferingMinSize);
 +        Long rootDiskOfferingId = offering.getDiskOfferingId();
 +        if (isIso) {
 +            if (diskOfferingId == null) {
 +                DiskOfferingVO diskOffering = 
_diskOfferingDao.findById(rootDiskOfferingId);
 +                if (diskOffering.isComputeOnly()) {
 +                    throw new InvalidParameterValueException("Installing from 
ISO requires a disk offering to be specified for the root disk.");
                  }
 -                size = diskSize * GiB_TO_BYTES;
              } else {
 -                size = diskOffering.getDiskSize();
 +                rootDiskOfferingId = diskOfferingId;
 +                diskOfferingId = null;
              }
 -            _volumeService.validateVolumeSizeInBytes(size);
 -            volumesSize += size;
 +        }
 +        if (!offering.getDiskOfferingStrictness() && overrideDiskOfferingId 
!= null) {
 +            rootDiskOfferingId = overrideDiskOfferingId;
 +        }
 +
 +        DiskOfferingVO rootdiskOffering = 
_diskOfferingDao.findById(rootDiskOfferingId);
-         long size = configureCustomRootDiskSize(customParameters, template, 
hypervisorType, rootdiskOffering);
++        long volumesSize = configureCustomRootDiskSize(customParameters, 
template, hypervisorType, rootdiskOffering);
 +
 +        if (!isIso && diskOfferingId != null) {
 +            DiskOfferingVO diskOffering = 
_diskOfferingDao.findById(diskOfferingId);
-             size += verifyAndGetDiskSize(diskOffering, diskSize);
++            volumesSize += verifyAndGetDiskSize(diskOffering, diskSize);
          }
          if (! VirtualMachineManager.ResourceCountRunningVMsonly.value()) {
              resourceLimitCheck(owner, isDisplayVm, new 
Long(offering.getCpu()), new Long(offering.getRamSize()));
@@@ -4134,24 -4164,6 +4133,29 @@@
          return vm;
      }
  
 +    private long verifyAndGetDiskSize(DiskOfferingVO diskOffering, Long 
diskSize) {
 +        long size = 0l;
-         if (diskOffering != null && diskOffering.isCustomized() && 
!diskOffering.isComputeOnly()) {
++        if (diskOffering == null) {
++            throw new InvalidParameterValueException("Specified disk offering 
cannot be found");
++        }
++        if (diskOffering.isCustomized() && !diskOffering.isComputeOnly()) {
 +            if (diskSize == null) {
 +                throw new InvalidParameterValueException("This disk offering 
requires a custom size specified");
 +            }
 +            Long customDiskOfferingMaxSize = 
VolumeOrchestrationService.CustomDiskOfferingMaxSize.value();
 +            Long customDiskOfferingMinSize = 
VolumeOrchestrationService.CustomDiskOfferingMinSize.value();
 +            if ((diskSize < customDiskOfferingMinSize) || (diskSize > 
customDiskOfferingMaxSize)) {
 +                throw new InvalidParameterValueException("VM Creation failed. 
Volume size: " + diskSize + "GB is out of allowed range. Max: " + 
customDiskOfferingMaxSize
 +                        + " Min:" + customDiskOfferingMinSize);
 +            }
-             size += diskSize * GiB_TO_BYTES;
++            size = diskSize * GiB_TO_BYTES;
++        } else {
++            size = diskOffering.getDiskSize();
 +        }
-         size += diskOffering.getDiskSize();
++        _volumeService.validateVolumeSizeInBytes(size);
 +        return size;
 +    }
 +
      @Override
      public boolean checkIfDynamicScalingCanBeEnabled(VirtualMachine vm, 
ServiceOffering offering, VirtualMachineTemplate template, Long zoneId) {
          boolean canEnableDynamicScaling = (vm != null ? 
vm.isDynamicallyScalable() : true) && offering.isDynamicScalingEnabled() && 
template.isDynamicallyScalable() && 
UserVmManager.EnableDynamicallyScaleVm.valueIn(zoneId);
@@@ -4166,10 -4178,12 +4170,11 @@@
       * Configures the Root disk size via User`s custom parameters.
       * If the Service Offering has the Root Disk size field configured then 
the User`s root disk custom parameter is overwritten by the service offering.
       */
 -    protected long configureCustomRootDiskSize(Map<String, String> 
customParameters, VMTemplateVO template, HypervisorType hypervisorType, 
ServiceOfferingVO serviceOffering) {
 +    protected long configureCustomRootDiskSize(Map<String, String> 
customParameters, VMTemplateVO template, HypervisorType hypervisorType, 
DiskOfferingVO rootDiskOffering) {
          verifyIfHypervisorSupportsRootdiskSizeOverride(hypervisorType);
 -        DiskOfferingVO diskOffering = 
_diskOfferingDao.findById(serviceOffering.getId());
 -        long rootDiskSizeInBytes = diskOffering.getDiskSize();
 +        long rootDiskSizeInBytes = verifyAndGetDiskSize(rootDiskOffering, 
NumbersUtil.parseLong(customParameters.get(VmDetailConstants.ROOT_DISK_SIZE), 
-1));
          if (rootDiskSizeInBytes > 0) { //if the size at DiskOffering is not 
zero then the Service Offering had it configured, it holds priority over the 
User custom size
+             _volumeService.validateVolumeSizeInBytes(rootDiskSizeInBytes);
              long rootDiskSizeInGiB = rootDiskSizeInBytes / GiB_TO_BYTES;
              customParameters.put(VmDetailConstants.ROOT_DISK_SIZE, 
String.valueOf(rootDiskSizeInGiB));
              return rootDiskSizeInBytes;
diff --cc server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
index c9c07c4,c9460c3..0660388
--- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
+++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
@@@ -71,8 -64,15 +64,14 @@@ import com.cloud.network.dao.NetworkVO
  import com.cloud.offering.ServiceOffering;
  import com.cloud.service.ServiceOfferingVO;
  import com.cloud.service.dao.ServiceOfferingDao;
+ import com.cloud.storage.DiskOfferingVO;
  import com.cloud.storage.GuestOSVO;
 -import com.cloud.storage.Storage;
+ import com.cloud.storage.VMTemplateVO;
+ import com.cloud.storage.VolumeApiService;
+ import com.cloud.storage.VolumeVO;
+ import com.cloud.storage.dao.DiskOfferingDao;
  import com.cloud.storage.dao.GuestOSDao;
+ import com.cloud.storage.dao.VMTemplateDao;
  import com.cloud.user.Account;
  import com.cloud.user.AccountManager;
  import com.cloud.user.AccountVO;
@@@ -474,7 -483,9 +478,8 @@@ public class UserVmManagerImplTest 
  
          
Mockito.when(diskfferingVo.getDiskSize()).thenReturn(offeringRootDiskSize);
  
+         
Mockito.when(volumeApiService.validateVolumeSizeInBytes(Mockito.anyLong())).thenReturn(true);
 -        long rootDiskSize = 
userVmManagerImpl.configureCustomRootDiskSize(customParameters, template, 
Hypervisor.HypervisorType.KVM, offering);
 +        long rootDiskSize = 
userVmManagerImpl.configureCustomRootDiskSize(customParameters, template, 
Hypervisor.HypervisorType.KVM, diskfferingVo);
  
          Assert.assertEquals(expectedRootDiskSize, rootDiskSize);
          Mockito.verify(userVmManagerImpl, 
Mockito.times(timesVerifyIfHypervisorSupports)).verifyIfHypervisorSupportsRootdiskSizeOverride(Mockito.any());

Reply via email to