Copilot commented on code in PR #12970:
URL: https://github.com/apache/cloudstack/pull/12970#discussion_r3049389804


##########
ui/src/views/tools/ImportUnmanagedInstance.vue:
##########
@@ -184,12 +190,13 @@
               </a-form-item>
               <a-form-item name="convertstorageoption" 
ref="convertstorageoption">
                 <check-box-select-pair
+                  :key="`convertstorageoption-${form.usevddk ? 'vddk' : 
'default'}-${switches.forceConvertToPool ? 'pool' : 'tmp'}`"
                   layout="vertical"
                   v-if="cluster.hypervisortype === 'KVM' && 
selectedVmwareVcenter"
                   :resourceKey="cluster.id"
                   :selectOptions="storageOptionsForConversion"
                   :checkBoxLabel="switches.forceConvertToPool ? 
$t('message.select.destination.storage.instance.conversion') : 
$t('message.select.temporary.storage.instance.conversion')"
-                  :defaultCheckBoxValue="false"
+                  :defaultCheckBoxValue="form.usevddk"
                   :reversed="false"
                   
@handle-checkselectpair-change="updateSelectedStorageOptionForConversion"

Review Comment:
   `:defaultCheckBoxValue="form.usevddk"` will render the checkbox as checked 
when VDDK is enabled, but `CheckBoxSelectPair` only sets `checked` in 
`created()` and does not initialize `selectedOption` or emit 
`handle-checkselectpair-change` unless the user toggles the checkbox. This can 
leave `selectedStorageOptionForConversion` unset and prevent storage pools from 
being loaded while the UI appears selected. Consider explicitly calling 
`updateSelectedStorageOptionForConversion(...)` from `onUseVddkChange`, or 
updating `CheckBoxSelectPair` to initialize `selectedOption` + emit when 
`defaultCheckBoxValue` starts as true.



##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java:
##########
@@ -275,4 +299,194 @@ protected void addExtraParamsToScript(String extraParams, 
Script script) {
     protected String encodeUsername(String username) {
         return URLEncoder.encode(username, Charset.defaultCharset());
     }
+
+    private String resolveVddkSetting(String commandValue, String agentValue) {
+        return 
StringUtils.defaultIfBlank(StringUtils.trimToNull(commandValue), 
StringUtils.trimToNull(agentValue));
+    }
+
+    protected boolean performInstanceConversionVddk(RemoteInstanceTO 
vmwareInstance, String originalVMName,
+                                                    String 
temporaryConvertFolder, String vddkLibDir,
+                                                    String libguestfsBackend, 
String vddkTransports,
+                                                    String 
configuredVddkThumbprint,
+                                                    long timeout, boolean 
verboseModeEnabled, String extraParams,
+                                                    String 
temporaryConvertUuid, String passwordOption) {
+
+        String vcenterPassword = vmwareInstance.getVcenterPassword();
+        if (StringUtils.isBlank(vcenterPassword)) {
+            logger.error("({}) Could not determine vCenter password for {}", 
originalVMName, vmwareInstance.getVcenterHost());
+            return false;
+        }
+
+        String passwordFilePath = String.format("/root/v2v.pass.cloud.%s",
+                StringUtils.defaultIfBlank(vmwareInstance.getVcenterHost(), 
"unknown"));
+        try {
+            Files.writeString(Path.of(passwordFilePath), vcenterPassword);
+            logger.debug("({}) Written vCenter password to {}", 
originalVMName, passwordFilePath);
+        } catch (Exception e) {
+            logger.error("({}) Failed to write vCenter password file {}: {}", 
originalVMName, passwordFilePath, e.getMessage());
+            return false;
+        }
+
+        String vpxUrl = buildVpxUrl(vmwareInstance, originalVMName);
+
+        StringBuilder cmd = new StringBuilder();
+
+        String effectiveLibguestfsBackend = 
StringUtils.defaultIfBlank(libguestfsBackend, "direct");
+        cmd.append("export 
LIBGUESTFS_BACKEND=").append(effectiveLibguestfsBackend).append(" && ");
+
+        cmd.append("virt-v2v ");
+        cmd.append("--root first ");
+        cmd.append("-ic '").append(vpxUrl).append("' ");
+        if (StringUtils.isBlank(passwordOption)) {
+            logger.error("({}) Could not determine supported password file 
option for virt-v2v", originalVMName);
+            return false;
+        }
+
+        cmd.append(passwordOption).append(" 
").append(passwordFilePath).append(" ");
+        cmd.append("-it vddk ");
+        cmd.append("-io vddk-libdir=").append(vddkLibDir).append(" ");
+        String vddkThumbprint = 
StringUtils.trimToNull(configuredVddkThumbprint);
+        if (StringUtils.isBlank(vddkThumbprint)) {
+            vddkThumbprint = 
getVcenterThumbprint(vmwareInstance.getVcenterHost(), timeout, originalVMName);
+        }
+        if (StringUtils.isBlank(vddkThumbprint)) {
+            logger.error("({}) Could not determine vCenter thumbprint for {}", 
originalVMName, vmwareInstance.getVcenterHost());
+            return false;
+        }
+        cmd.append("-io vddk-thumbprint=").append(vddkThumbprint).append(" ");
+        if (StringUtils.isNotBlank(vddkTransports)) {
+            cmd.append("-io vddk-transports=").append(vddkTransports).append(" 
");
+        }
+        cmd.append(originalVMName).append(" ");
+        cmd.append("-o local ");
+        cmd.append("-os ").append(temporaryConvertFolder).append(" ");
+        cmd.append("-of qcow2 ");
+        cmd.append("-on ").append(temporaryConvertUuid).append(" ");
+
+        if (verboseModeEnabled) {
+            cmd.append("-v ");
+        }
+
+        if (StringUtils.isNotBlank(extraParams)) {
+            cmd.append(extraParams).append(" ");
+        }
+
+        Script script = new Script("/bin/bash", timeout, logger);
+        script.add("-c");
+        script.add(cmd.toString());
+
+        String logPrefix = String.format("(%s) virt-v2v vddk import", 
originalVMName);
+        OutputInterpreter.LineByLineOutputLogger outputLogger =
+                new OutputInterpreter.LineByLineOutputLogger(logger, 
logPrefix);
+
+        logger.info("({}) Starting virt-v2v VDDK conversion", originalVMName);
+        script.execute(outputLogger);
+
+        int exitValue = script.getExitValue();
+        if (exitValue != 0) {
+            logger.error("({}) virt-v2v failed with exit code {}", 
originalVMName, exitValue);
+        }
+        try {
+            Files.deleteIfExists(Path.of(passwordFilePath));
+            logger.debug("({}) Deleted password file {}", originalVMName, 
passwordFilePath);
+        } catch (Exception e) {
+            logger.warn("({}) Failed to delete password file {}: {}", 
originalVMName, passwordFilePath, e.getMessage());
+        }
+
+        return exitValue == 0;
+    }
+
+
+    protected String getVcenterThumbprint(String vcenterHost, long timeout, 
String originalVMName) {
+        if (StringUtils.isBlank(vcenterHost)) {
+            return null;
+        }
+
+        String endpoint = String.format("%s:443", vcenterHost);
+        String command = String.format("openssl s_client -connect '%s' 
</dev/null 2>/dev/null | " +
+                "openssl x509 -fingerprint -sha1 -noout", endpoint);
+
+        Script script = new Script("/bin/bash", timeout, logger);
+        script.add("-c");
+        script.add(command);
+
+        OutputInterpreter.AllLinesParser parser = new 
OutputInterpreter.AllLinesParser();
+        script.execute(parser);
+
+        String output = parser.getLines();
+        if (script.getExitValue() != 0) {
+            logger.error("({}) Failed to fetch vCenter thumbprint for {}", 
originalVMName, vcenterHost);
+            return null;
+        }
+
+        String thumbprint = extractSha1Fingerprint(output);
+        if (StringUtils.isBlank(thumbprint)) {
+            logger.error("({}) Failed to parse vCenter thumbprint from output 
for {}", originalVMName, vcenterHost);
+            return null;
+        }
+        return thumbprint;
+    }
+
+    private String extractSha1Fingerprint(String output) {
+        String parsedOutput = StringUtils.trimToEmpty(output);
+        if (StringUtils.isBlank(parsedOutput)) {
+            return null;
+        }
+
+        for (String line : parsedOutput.split("\\R")) {
+            String trimmedLine = StringUtils.trimToEmpty(line);
+            if (StringUtils.isBlank(trimmedLine)) {
+                continue;
+            }
+
+            Matcher matcher = SHA1_FINGERPRINT_PATTERN.matcher(trimmedLine);
+            if (matcher.find()) {
+                return matcher.group(1).toUpperCase(Locale.ROOT);
+            }
+
+            // Fallback for raw fingerprint-only output.
+            if (trimmedLine.matches("(?i)[0-9a-f]{2}(:[0-9a-f]{2})+")) {
+                return trimmedLine.toUpperCase(Locale.ROOT);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Build vpx:// URL for virt-v2v
+     *
+     * Format:
+     * vpx://user@vcenter/DC/cluster/host?no_verify=1
+     */
+    private String buildVpxUrl(RemoteInstanceTO vmwareInstance, String 
originalVMName) {
+
+        String vcenter = vmwareInstance.getVcenterHost();
+        String username = vmwareInstance.getVcenterUsername();
+        String datacenter = vmwareInstance.getDatacenterName();
+        String cluster = vmwareInstance.getClusterName();
+        String host = vmwareInstance.getHostName();
+
+        String encodedUsername = encodeUsername(username);
+
+        StringBuilder url = new StringBuilder();
+        url.append("vpx://")
+                .append(encodedUsername)
+                .append("@")
+                .append(vcenter)
+                .append("/")
+                .append(datacenter);
+
+        if (StringUtils.isNotBlank(cluster)) {
+            url.append("/").append(cluster);
+        }
+
+        if (StringUtils.isNotBlank(host)) {
+            url.append("/").append(host);
+        }
+
+        url.append("?no_verify=1");
+
+        logger.info("({}) Using VPX URL: {}", originalVMName, url);
+        return url.toString();

Review Comment:
   `buildVpxUrl` appends `?no_verify=1` and logs the full VPX URL (including 
the username) at INFO. Disabling verification undermines the thumbprint-based 
trust model, and logging credentials-related identifiers is risky. Consider 
removing `no_verify=1` if not strictly required, URL-encoding 
datacenter/cluster/host path segments, and sanitizing logs (e.g., log only 
vCenter host + datacenter, or redact the username).



##########
server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java:
##########
@@ -1730,7 +1743,7 @@ protected UserVm 
importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster
                 convertedInstance = 
convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(
                         sourceVMName, sourceVMwareInstance, convertHost, 
importHost,
                         convertStoragePools, serviceOffering, 
dataDiskOfferingMap,
-                        temporaryConvertLocation, vcenter, username, password, 
datacenterName, forceConvertToPool, extraParams);
+                        temporaryConvertLocation, vcenter, username, password, 
datacenterName, forceConvertToPool, extraParams, useVddk, details);

Review Comment:
   The comment and log message in this branch say it always performs OVF export 
through ovftool, but this same path is now also used when `useVddk` is true 
(which does not export OVF). This is misleading for troubleshooting and future 
maintenance; update the comment/logs (or split into a dedicated VDDK method) so 
the selected conversion strategy is accurately reflected.



##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java:
##########
@@ -5946,6 +5988,40 @@ public boolean hostSupportsOvfExport() {
         return exitValue == 0;
     }
 
+    /**
+     * Detect which password option virt-v2v supports by examining its --help 
output
+     * @return "-ip" if supported (virt-v2v >= 2.8.1), "--password-file" if 
older version, or null if detection fails
+     */
+    protected String detectPasswordFileOption() {
+        try {
+            ProcessBuilder pb = new ProcessBuilder("virt-v2v", "--help");
+            Process process = pb.start();
+
+            String output = new 
String(process.getInputStream().readAllBytes());
+            process.waitFor();
+
+            if (output.contains("-ip <filename>")) {
+                return "-ip";
+            } else if (output.contains("--password-file")) {
+                return "--password-file";
+            } else {
+                LOGGER.error("virt-v2v does not support -ip or 
--password-file");
+                return null;
+            }
+        } catch (Exception e) {
+            LOGGER.error("Failed to detect virt-v2v password option: {}", 
e.getMessage());
+            return null;
+        }

Review Comment:
   `detectPasswordFileOption()` runs `virt-v2v --help` via `ProcessBuilder` 
without a timeout and without consuming stderr; if `virt-v2v` hangs or produces 
enough stderr output, agent startup could block indefinitely. Consider using 
the existing `Script` helper with a timeout (and redirected stderr), and 
check/handle the process exit code before trusting output.
   ```suggestion
           final int timeout = 30;
           final Script script = new Script("/bin/bash", timeout, LOGGER);
           script.add("-c");
           script.add("virt-v2v --help 2>&1");
   
           final AllLinesParser parser = new AllLinesParser();
           final String executionResult = script.execute(parser);
           if (executionResult != null) {
               LOGGER.error("Failed to detect virt-v2v password option: {}", 
executionResult);
               return null;
           }
   
           final String output = parser.getLines();
           if (StringUtils.isBlank(output)) {
               LOGGER.error("Failed to detect virt-v2v password option: empty 
output from virt-v2v --help");
               return null;
           }
   
           if (output.contains("-ip <filename>")) {
               return "-ip";
           } else if (output.contains("--password-file")) {
               return "--password-file";
           } else {
               LOGGER.error("virt-v2v does not support -ip or --password-file");
               return null;
           }
   ```



##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java:
##########
@@ -275,4 +299,194 @@ protected void addExtraParamsToScript(String extraParams, 
Script script) {
     protected String encodeUsername(String username) {
         return URLEncoder.encode(username, Charset.defaultCharset());
     }
+
+    private String resolveVddkSetting(String commandValue, String agentValue) {
+        return 
StringUtils.defaultIfBlank(StringUtils.trimToNull(commandValue), 
StringUtils.trimToNull(agentValue));
+    }
+
+    protected boolean performInstanceConversionVddk(RemoteInstanceTO 
vmwareInstance, String originalVMName,
+                                                    String 
temporaryConvertFolder, String vddkLibDir,
+                                                    String libguestfsBackend, 
String vddkTransports,
+                                                    String 
configuredVddkThumbprint,
+                                                    long timeout, boolean 
verboseModeEnabled, String extraParams,
+                                                    String 
temporaryConvertUuid, String passwordOption) {
+
+        String vcenterPassword = vmwareInstance.getVcenterPassword();
+        if (StringUtils.isBlank(vcenterPassword)) {
+            logger.error("({}) Could not determine vCenter password for {}", 
originalVMName, vmwareInstance.getVcenterHost());
+            return false;
+        }
+
+        String passwordFilePath = String.format("/root/v2v.pass.cloud.%s",
+                StringUtils.defaultIfBlank(vmwareInstance.getVcenterHost(), 
"unknown"));
+        try {
+            Files.writeString(Path.of(passwordFilePath), vcenterPassword);
+            logger.debug("({}) Written vCenter password to {}", 
originalVMName, passwordFilePath);
+        } catch (Exception e) {
+            logger.error("({}) Failed to write vCenter password file {}: {}", 
originalVMName, passwordFilePath, e.getMessage());
+            return false;
+        }
+
+        String vpxUrl = buildVpxUrl(vmwareInstance, originalVMName);
+
+        StringBuilder cmd = new StringBuilder();
+
+        String effectiveLibguestfsBackend = 
StringUtils.defaultIfBlank(libguestfsBackend, "direct");
+        cmd.append("export 
LIBGUESTFS_BACKEND=").append(effectiveLibguestfsBackend).append(" && ");
+
+        cmd.append("virt-v2v ");
+        cmd.append("--root first ");
+        cmd.append("-ic '").append(vpxUrl).append("' ");
+        if (StringUtils.isBlank(passwordOption)) {
+            logger.error("({}) Could not determine supported password file 
option for virt-v2v", originalVMName);
+            return false;
+        }
+
+        cmd.append(passwordOption).append(" 
").append(passwordFilePath).append(" ");
+        cmd.append("-it vddk ");
+        cmd.append("-io vddk-libdir=").append(vddkLibDir).append(" ");
+        String vddkThumbprint = 
StringUtils.trimToNull(configuredVddkThumbprint);
+        if (StringUtils.isBlank(vddkThumbprint)) {
+            vddkThumbprint = 
getVcenterThumbprint(vmwareInstance.getVcenterHost(), timeout, originalVMName);
+        }
+        if (StringUtils.isBlank(vddkThumbprint)) {
+            logger.error("({}) Could not determine vCenter thumbprint for {}", 
originalVMName, vmwareInstance.getVcenterHost());
+            return false;
+        }
+        cmd.append("-io vddk-thumbprint=").append(vddkThumbprint).append(" ");
+        if (StringUtils.isNotBlank(vddkTransports)) {
+            cmd.append("-io vddk-transports=").append(vddkTransports).append(" 
");
+        }
+        cmd.append(originalVMName).append(" ");
+        cmd.append("-o local ");
+        cmd.append("-os ").append(temporaryConvertFolder).append(" ");
+        cmd.append("-of qcow2 ");
+        cmd.append("-on ").append(temporaryConvertUuid).append(" ");
+
+        if (verboseModeEnabled) {
+            cmd.append("-v ");
+        }
+
+        if (StringUtils.isNotBlank(extraParams)) {
+            cmd.append(extraParams).append(" ");
+        }
+
+        Script script = new Script("/bin/bash", timeout, logger);
+        script.add("-c");
+        script.add(cmd.toString());
+

Review Comment:
   The VDDK path builds a single shell string and runs it via `/bin/bash -c`, 
concatenating values like VM name, paths, transports, and `extraParams`. 
Several of these are user/DB configurable and are not shell-escaped, which can 
break conversions for names with spaces/special chars and also introduces 
command-injection risk. Prefer constructing a `Script("virt-v2v", ...)` and 
passing arguments with `script.add(...)` (similar to 
`performInstanceConversion`), and set `LIBGUESTFS_BACKEND` via the env-map 
rather than `export ... &&`.



##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java:
##########
@@ -275,4 +299,194 @@ protected void addExtraParamsToScript(String extraParams, 
Script script) {
     protected String encodeUsername(String username) {
         return URLEncoder.encode(username, Charset.defaultCharset());
     }
+
+    private String resolveVddkSetting(String commandValue, String agentValue) {
+        return 
StringUtils.defaultIfBlank(StringUtils.trimToNull(commandValue), 
StringUtils.trimToNull(agentValue));
+    }
+
+    protected boolean performInstanceConversionVddk(RemoteInstanceTO 
vmwareInstance, String originalVMName,
+                                                    String 
temporaryConvertFolder, String vddkLibDir,
+                                                    String libguestfsBackend, 
String vddkTransports,
+                                                    String 
configuredVddkThumbprint,
+                                                    long timeout, boolean 
verboseModeEnabled, String extraParams,
+                                                    String 
temporaryConvertUuid, String passwordOption) {
+
+        String vcenterPassword = vmwareInstance.getVcenterPassword();
+        if (StringUtils.isBlank(vcenterPassword)) {
+            logger.error("({}) Could not determine vCenter password for {}", 
originalVMName, vmwareInstance.getVcenterHost());
+            return false;
+        }
+
+        String passwordFilePath = String.format("/root/v2v.pass.cloud.%s",
+                StringUtils.defaultIfBlank(vmwareInstance.getVcenterHost(), 
"unknown"));
+        try {
+            Files.writeString(Path.of(passwordFilePath), vcenterPassword);
+            logger.debug("({}) Written vCenter password to {}", 
originalVMName, passwordFilePath);
+        } catch (Exception e) {
+            logger.error("({}) Failed to write vCenter password file {}: {}", 
originalVMName, passwordFilePath, e.getMessage());
+            return false;
+        }
+
+        String vpxUrl = buildVpxUrl(vmwareInstance, originalVMName);
+
+        StringBuilder cmd = new StringBuilder();
+
+        String effectiveLibguestfsBackend = 
StringUtils.defaultIfBlank(libguestfsBackend, "direct");
+        cmd.append("export 
LIBGUESTFS_BACKEND=").append(effectiveLibguestfsBackend).append(" && ");
+
+        cmd.append("virt-v2v ");
+        cmd.append("--root first ");
+        cmd.append("-ic '").append(vpxUrl).append("' ");
+        if (StringUtils.isBlank(passwordOption)) {
+            logger.error("({}) Could not determine supported password file 
option for virt-v2v", originalVMName);
+            return false;
+        }
+
+        cmd.append(passwordOption).append(" 
").append(passwordFilePath).append(" ");
+        cmd.append("-it vddk ");
+        cmd.append("-io vddk-libdir=").append(vddkLibDir).append(" ");
+        String vddkThumbprint = 
StringUtils.trimToNull(configuredVddkThumbprint);
+        if (StringUtils.isBlank(vddkThumbprint)) {
+            vddkThumbprint = 
getVcenterThumbprint(vmwareInstance.getVcenterHost(), timeout, originalVMName);
+        }
+        if (StringUtils.isBlank(vddkThumbprint)) {
+            logger.error("({}) Could not determine vCenter thumbprint for {}", 
originalVMName, vmwareInstance.getVcenterHost());
+            return false;
+        }

Review Comment:
   `performInstanceConversionVddk` writes the vCenter password to a fixed path 
under `/root` derived from the vCenter host. This is vulnerable to races 
between concurrent conversions (overwriting/deleting each other’s password 
file) and may leave the password on disk on early returns (e.g., when 
`passwordOption` is blank or thumbprint lookup fails). Use a unique temp file 
(e.g., `Files.createTempFile`), set restrictive permissions (0600), and ensure 
deletion happens in a `finally` block that runs for all exit paths.



##########
server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java:
##########
@@ -1785,6 +1798,41 @@ protected void checkConversionStoragePool(Long 
convertStoragePoolId, boolean for
         }
     }
 
+    protected void validateSelectedConversionStoragePoolForVddk(boolean 
useVddk, Long convertStoragePoolId,
+                                                                
ServiceOfferingVO serviceOffering, Map<String, Long> dataDiskOfferingMap) {
+        if (!useVddk || convertStoragePoolId == null) {
+            return;
+        }
+
+        StoragePoolVO selectedStoragePool = 
primaryDataStoreDao.findById(convertStoragePoolId);
+        if (selectedStoragePool == null) {
+            return;
+        }
+
+        if (serviceOffering.getDiskOfferingId() != null) {
+            DiskOfferingVO rootDiskOffering = 
diskOfferingDao.findById(serviceOffering.getDiskOfferingId());
+            if (rootDiskOffering == null) {
+                throw new InvalidParameterValueException(String.format("Cannot 
find disk offering with ID %s that belongs to the service offering %s",
+                        serviceOffering.getDiskOfferingId(), 
serviceOffering.getName()));
+            }
+            if 
(!volumeApiService.doesStoragePoolSupportDiskOffering(selectedStoragePool, 
rootDiskOffering)) {
+                throw new InvalidParameterValueException("Using VDDK, multiple 
storage pools cannot be used if a storage pool is selected for conversion");
+            }
+        }
+
+        if (MapUtils.isNotEmpty(dataDiskOfferingMap)) {
+            for (Long diskOfferingId : dataDiskOfferingMap.values()) {
+                DiskOfferingVO diskOffering = 
diskOfferingDao.findById(diskOfferingId);
+                if (diskOffering == null) {
+                    throw new 
InvalidParameterValueException(String.format("Cannot find disk offering with ID 
%s", diskOfferingId));
+                }
+                if 
(!volumeApiService.doesStoragePoolSupportDiskOffering(selectedStoragePool, 
diskOffering)) {
+                    throw new InvalidParameterValueException("Using VDDK, 
multiple storage pools cannot be used if a storage pool is selected for 
conversion");
+                }
+            }

Review Comment:
   `validateSelectedConversionStoragePoolForVddk` throws "Using VDDK, multiple 
storage pools cannot be used if a storage pool is selected for conversion" when 
a disk offering is unsupported by the selected pool. The message doesn’t match 
the actual failure condition (disk offering incompatibility) and will be 
confusing to users. Consider a clearer message that names the selected pool and 
the disk offering that isn’t supported.



-- 
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]

Reply via email to