Copilot commented on code in PR #13101:
URL: https://github.com/apache/cloudstack/pull/13101#discussion_r3186080703
##########
server/src/main/java/com/cloud/template/TemplateManagerImpl.java:
##########
@@ -1402,20 +1445,114 @@ private boolean attachISOToVM(long vmId, long isoId,
boolean attach, boolean for
return (a != null && a.getResult());
}
- private boolean attachISOToVM(long vmId, long userId, long isoId, boolean
attach, boolean forced, boolean isVirtualRouter) {
+ boolean attachISOToVM(long vmId, long userId, long isoId, boolean attach,
boolean forced, boolean isVirtualRouter) {
UserVmVO vm = _userVmDao.findById(vmId);
VMTemplateVO iso = _tmpltDao.findById(isoId);
- boolean success = attachISOToVM(vmId, isoId, attach, forced,
isVirtualRouter);
- if (success && attach && !isVirtualRouter) {
+ int targetSlot = attach ? chooseAttachSlot(vmId, vm) :
findAttachedSlot(vmId, vm, isoId);
+ boolean success = attachISOToVM(vmId, isoId, targetSlot, attach,
forced, isVirtualRouter);
+ if (!success || isVirtualRouter) {
+ return success;
+ }
+ if (attach) {
+ persistIsoAttachment(vmId, vm, iso, targetSlot);
+ } else {
+ persistIsoDetachment(vmId, vm, isoId, targetSlot);
+ }
+ return success;
+ }
+
+ private int chooseAttachSlot(long vmId, UserVmVO vm) {
+ if (vm.getIsoId() == null) {
+ return CDROM_PRIMARY_DEVICE_SEQ;
+ }
+ VmIsoMapVO highest = highestCdromMapEntry(vmId);
+ return highest == null ? CDROM_PRIMARY_DEVICE_SEQ + 1 :
highest.getDeviceSeq() + 1;
+ }
+
+ private int findAttachedSlot(long vmId, UserVmVO vm, long isoId) {
+ if (vm.getIsoId() != null && vm.getIsoId() == isoId) {
+ return CDROM_PRIMARY_DEVICE_SEQ;
+ }
+ VmIsoMapVO entry = _vmIsoMapDao.findByVmIdIsoId(vmId, isoId);
+ return entry != null ? entry.getDeviceSeq() : CDROM_PRIMARY_DEVICE_SEQ;
+ }
+
+ private void persistIsoAttachment(long vmId, UserVmVO vm, VMTemplateVO
iso, int slot) {
+ if (slot == CDROM_PRIMARY_DEVICE_SEQ) {
vm.setIsoId(iso.getId());
_userVmDao.update(vmId, vm);
+ } else {
+ _vmIsoMapDao.persist(new VmIsoMapVO(vmId, iso.getId(), slot));
}
- if (success && !attach && !isVirtualRouter) {
+ }
+
+ private void persistIsoDetachment(long vmId, UserVmVO vm, long isoId, int
slot) {
+ if (slot == CDROM_PRIMARY_DEVICE_SEQ) {
vm.setIsoId(null);
_userVmDao.update(vmId, vm);
+ return;
}
- return success;
+ VmIsoMapVO entry = _vmIsoMapDao.findByVmIdIsoId(vmId, isoId);
+ if (entry != null) {
+ _vmIsoMapDao.remove(entry.getId());
+ }
+ }
+
+ VmIsoMapVO highestCdromMapEntry(long vmId) {
+ VmIsoMapVO highest = null;
+ for (VmIsoMapVO row : _vmIsoMapDao.listByVmId(vmId)) {
+ if (highest == null || row.getDeviceSeq() >
highest.getDeviceSeq()) {
+ highest = row;
+ }
+ }
+ return highest;
+ }
+
+ Long resolveIsoIdForDetach(Long primaryIsoId, List<VmIsoMapVO> extras,
Long isoParamId) {
+ if (isoParamId != null) {
+ boolean attached = (primaryIsoId != null &&
primaryIsoId.equals(isoParamId))
+ || extras.stream().anyMatch(r -> r.getIsoId() ==
isoParamId);
+ if (!attached) {
+ throw new InvalidParameterValueException("The specified ISO is
not attached to this Instance.");
+ }
+ return isoParamId;
+ }
+ int totalAttached = (primaryIsoId != null ? 1 : 0) + extras.size();
+ if (totalAttached == 0) {
+ throw new InvalidParameterValueException("The specified instance
has no ISO attached to it.");
+ }
+ if (totalAttached > 1) {
+ throw new InvalidParameterValueException("Instance has more than
one ISO attached; specify the 'id' parameter to choose which to detach.");
+ }
+ return primaryIsoId != null ? primaryIsoId : extras.get(0).getIsoId();
+ }
+
+ boolean isIsoAlreadyAttached(long vmId, Long primaryIsoId, long isoId) {
+ if (primaryIsoId != null && primaryIsoId.equals(isoId)) {
+ return true;
+ }
+ return _vmIsoMapDao.findByVmIdIsoId(vmId, isoId) != null;
+ }
+
+ private void enforceCdromAttachLimits(long vmId, UserVm vm, long isoId) {
+ Long primaryIsoId = vm.getIsoId();
+ if (isIsoAlreadyAttached(vmId, primaryIsoId, isoId)) {
+ throw new InvalidParameterValueException("The specified ISO is
already attached to this Instance.");
+ }
+ int effectiveMax = effectiveMaxCdroms(vm);
+ int attached = (primaryIsoId != null ? 1 : 0) +
_vmIsoMapDao.listByVmId(vmId).size();
+ if (attached >= effectiveMax) {
+ throw new InvalidParameterValueException(String.format(
Review Comment:
With `vm_iso_map` introduced, an ISO can be "in use" even when it is not
referenced by `user_vm.iso_id` (e.g. attached in a secondary slot). However,
ISO deletion still relies on `UserVmJoinDao.listActiveByIsoId(...)` (which is
based on the primary ISO only), so an ISO attached via `vm_iso_map` may be
incorrectly considered deleteable. Please update the deleteability check to
also consider `vm_iso_map` attachments (ideally filtering by active VM state).
##########
server/src/main/java/com/cloud/template/TemplateManagerImpl.java:
##########
@@ -679,45 +684,73 @@ private String extract(Account caller, Long templateId,
String url, Long zoneId,
@Override
public void prepareIsoForVmProfile(VirtualMachineProfile profile,
DeployDestination dest) {
UserVmVO vm = _userVmDao.findById(profile.getId());
- if (vm.getIsoId() != null) {
- Map<Volume, StoragePool> storageForDisks =
dest.getStorageForDisks();
- Long poolId = null;
- TemplateInfo template;
- if (MapUtils.isNotEmpty(storageForDisks)) {
- for (StoragePool storagePool : storageForDisks.values()) {
- if (poolId != null && storagePool.getId() != poolId) {
- throw new CloudRuntimeException("Cannot determine
where to download ISO");
- }
- poolId = storagePool.getId();
+ Long poolId = singleStoragePoolId(dest);
+ Map<Integer, Long> slotToIsoId = loadAttachedIsoSlots(vm);
Review Comment:
`prepareIsoForVmProfile` now always calls `singleStoragePoolId(dest)` even
when the VM has no attached ISO(s). This can throw "Cannot determine where to
download ISO" for VMs that have volumes on multiple storage pools, even though
no ISO download/prep is needed. Consider only resolving/enforcing a single pool
when at least one ISO slot is actually populated (or when a direct-download ISO
requires a pool), and otherwise skip pool resolution entirely.
##########
server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java:
##########
@@ -244,6 +250,20 @@ public UserVmResponse newUserVmResponse(ResponseView view,
String objectName, Us
userVmResponse.setIsoId(userVm.getIsoUuid());
userVmResponse.setIsoName(userVm.getIsoName());
userVmResponse.setIsoDisplayText(userVm.getIsoDisplayText());
+
+ List<AttachedIsoResponse> attachedIsos = new ArrayList<>();
+ if (userVm.getIsoUuid() != null) {
+ attachedIsos.add(new AttachedIsoResponse(userVm.getIsoUuid(),
userVm.getIsoName(),
+ userVm.getIsoDisplayText(),
TemplateManager.CDROM_PRIMARY_DEVICE_SEQ));
+ }
+ for (VmIsoMapVO row : vmIsoMapDao.listByVmId(userVm.getId())) {
+ VMTemplateVO tmpl = vmTemplateDao.findById(row.getIsoId());
+ if (tmpl != null) {
+ attachedIsos.add(new AttachedIsoResponse(tmpl.getUuid(),
tmpl.getName(),
+ tmpl.getDisplayText(), row.getDeviceSeq()));
+ }
+ }
Review Comment:
`newUserVmResponse` now performs `vmIsoMapDao.listByVmId(...)` and then
`vmTemplateDao.findById(...)` for each extra ISO. On large
`listVirtualMachines` responses this adds per-VM (and per-ISO) queries, which
can become an N+1 pattern. Consider batch-loading the extra ISO rows and
templates (e.g., prefetch map rows for all VM IDs in the page and bulk-fetch
templates by ISO IDs) or otherwise caching within the response build.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]