Hello CloudStack developers / users,

We hit a reproducible VMware attach failure on Apache CloudStack 4.23.0 with 
vCenter/vSphere 8.

Summary
=======
attachVolume fails with:
  Failed to attach volume ... to VM ...
Agent log shows:
  java.io.IOException: Server returned HTTP response code: 500
  for URL: https://<vcenter>/folder/<vm>/<volume>.vmdk?dcPath=...&dsName=...

Root cause (chicken-and-egg)
============================
In VirtualMachineMO.attachDisk(...), ACS calls updateVmdkAdapter(vmdk, 
controller)
*before* reconfigVMTask() adds the disk to the VM.

updateVmdkAdapter() -> getVmdkFileInfo() -> VmwareContext.getResourceContent()
downloads the VMDK descriptor via the vCenter datastore *folder* HTTP API.

On our vCenter 8, that /folder/ download returns HTTP 500 for VMDKs that are
*not yet attached* to any VM. The same VMDK downloads fine *after* it is 
attached
(verified with govc datastore.download).

So attach never reaches reconfigVMTask().

Related secondary issue
=======================
VmdkAdapterType.getType("lsilogicsas") returns "none", so 
updateAdapterTypeIfRequired()
on resize can rewrite ddb.adapterType from "lsilogicsas" to "lsilogic", which 
then
interacts badly with LSI Logic SAS controllers / capacity rewrite paths
(spec.deviceChange.device / capacityInKB vs capacityInBytes mismatches).

Environment
===========
- ACS: 4.23.0.0
- Hypervisor: VMware / vCenter 8
- Guest: Windows Server 2022 (UEFI Secure), root/dataDiskController=lsilogicsas
- Storage: VMFS (SAS / NVM)

Suggested upstream fix (preferred)
==================================
1) In VirtualMachineMO.attachDisk: move updateVmdkAdapter() to *after* a 
successful
   reconfigVMTask() (disk is attached => folder download works), OR
2) Make adapter rewrite best-effort: if getVmdkFileInfo() fails, log a warning 
and
   continue with attach (SCSI controller on the VM is what matters).

Also consider treating "lsilogicsas" as already-valid in 
updateAdapterTypeIfRequired()
(do not rewrite to lsilogic), since getType() currently maps it to none.

Proposed code change (best-effort skip)
=======================================
File: plugins/hypervisors/vmware/.../VirtualMachineMO.java
Methods: updateVmdkAdapter, updateAdapterTypeIfRequired

```java
public void updateVmdkAdapter(String vmdkFileName, String diskController) 
throws Exception {
    String currentAdapterType;
    DiskControllerType diskControllerType = 
DiskControllerType.getType(diskController);
    VmdkAdapterType vmdkAdapterType = 
VmdkAdapterType.getAdapterType(diskControllerType);
    if (vmdkAdapterType == VmdkAdapterType.none) {
        String message = "Failed to attach disk due to invalid vmdk adapter 
type for vmdk file ["
            + vmdkFileName + "] with controller : " + diskControllerType;
        logger.debug(message);
        throw new Exception(message);
    }
    String newAdapterType = vmdkAdapterType.toString();
    Pair<VmdkFileDescriptor, byte[]> vmdkInfo;
    try {
        vmdkInfo = this.getVmdkFileInfo(vmdkFileName);
    } catch (Exception e) {
        // vCenter /folder API can return HTTP 500 for VMDKs not yet attached 
to a VM.
        // Skip rewrite; controller type on the VM is what matters for attach.
        logger.warn("Skipping VMDK adapter update for " + vmdkFileName
            + " (descriptor unreadable, attaching anyway): " + e.getMessage());
        return;
    }
    VmdkFileDescriptor vmdkFileDescriptor = (VmdkFileDescriptor) 
vmdkInfo.first();
    boolean isVmfsSparseFile = vmdkFileDescriptor.isVmfsSparseFile();
    if (!isVmfsSparseFile
            && !(currentAdapterType = vmdkFileDescriptor.getAdapterType())
                .equalsIgnoreCase(newAdapterType)) {
        logger.info("Updating adapter type to " + newAdapterType
            + " for VMDK file " + vmdkFileName);
        Pair<DatacenterMO, String> dcInfo = this.getOwnerDatacenter();
        byte[] newVmdkContent = VmdkFileDescriptor.changeVmdkAdapterType(
            (byte[]) vmdkInfo.second(), newAdapterType);
        String vmdkUploadUrl = this.getContext().composeDatastoreBrowseUrl(
            ((DatacenterMO) dcInfo.first()).getName(), vmdkFileName);
        this.getContext().uploadResourceContent(vmdkUploadUrl, newVmdkContent);
        logger.info("Updated VMDK file " + vmdkFileName);
    }
}

public void updateAdapterTypeIfRequired(String vmdkFileName) throws Exception {
    Pair<VmdkFileDescriptor, byte[]> vmdkInfo;
    try {
        vmdkInfo = this.getVmdkFileInfo(vmdkFileName);
    } catch (Exception e) {
        logger.warn("Skipping updateAdapterTypeIfRequired for "
            + vmdkFileName + ": " + e.getMessage());
        return;
    }
    VmdkFileDescriptor vmdkFileDescriptor = (VmdkFileDescriptor) 
vmdkInfo.first();
    boolean isVmfsSparseFile = vmdkFileDescriptor.isVmfsSparseFile();
    if (!isVmfsSparseFile) {
        String currentAdapterTypeStr = vmdkFileDescriptor.getAdapterType();
        // Do not treat lsilogicsas as "none" and rewrite to lsilogic.
        if (currentAdapterTypeStr != null
                && currentAdapterTypeStr.equalsIgnoreCase("lsilogicsas")) {
            return;
        }
        if (logger.isTraceEnabled()) {
            logger.trace("Detected adapter type  " + currentAdapterTypeStr
                + " for VMDK file " + vmdkFileName);
        }
        VmdkAdapterType currentAdapterType =
            VmdkAdapterType.getType(currentAdapterTypeStr);
        if (currentAdapterType == VmdkAdapterType.none) {
            VmdkAdapterType newAdapterType = VmdkAdapterType.lsilogic;
            logger.debug("Updating adapter type to " + newAdapterType
                + " from " + currentAdapterTypeStr
                + " for VMDK file " + vmdkFileName);
            Pair<DatacenterMO, String> dcInfo = this.getOwnerDatacenter();
            byte[] newVmdkContent = VmdkFileDescriptor.changeVmdkAdapterType(
                (byte[]) vmdkInfo.second(), newAdapterType.toString());
            String vmdkUploadUrl = this.getContext().composeDatastoreBrowseUrl(
                ((DatacenterMO) dcInfo.first()).getName(), vmdkFileName);
            this.getContext().uploadResourceContent(vmdkUploadUrl, 
newVmdkContent);
            logger.debug("Updated VMDK file " + vmdkFileName);
        }
    }
}
```

Workaround we applied in production
===================================
Temporary bytecode NOP of updateVmdkAdapter / updateAdapterTypeIfRequired in
cloudstack-4.23.0.0.jar on the management server. After that, panel attachVolume
succeeds. Happy to open a GitHub PR / JIRA if useful.

Happy to provide more logs or test a patch build.

Thanks,
Umit Eyigun
TRTEK
[email protected]

Reply via email to