You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by we...@apache.org on 2026/08/26 09:33:02 UTC
(cloudstack) 01/01: vmware-base: fix template copy from secondary to primary
This is an automated email from the ASF dual-hosted git repository.
weizhouapache pushed a commit to branch main-fix-vmware-template
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit 66df898efcb24700c40cedb01ada40c0cbe1ef60
Author: Wei Zhou <we...@apache.org>
AuthorDate: Wed Aug 26 11:32:44 2026 +0200
vmware-base: fix template copy from secondary to primary
---
.../hypervisor/vmware/util/VmwareContext.java | 132 +++++++++++++++------
1 file changed, 99 insertions(+), 33 deletions(-)
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareContext.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareContext.java
index c2b34aea5d9..7e1d54b9a41 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareContext.java
+++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareContext.java
@@ -23,6 +23,7 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
@@ -387,25 +388,35 @@ public class VmwareContext {
OutputStream out = null;
InputStream in = null;
BufferedReader br = null;
+ long bytesWritten = 0;
try {
- out = conn.getOutputStream();
- in = new FileInputStream(localFileName);
- byte[] buf = new byte[ChunkSize];
- int len = 0;
- while ((len = in.read(buf)) > 0) {
- out.write(buf, 0, len);
+ try {
+ out = conn.getOutputStream();
+ in = new FileInputStream(localFileName);
+ byte[] buf = new byte[ChunkSize];
+ int len = 0;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ bytesWritten += len;
+ }
+ out.flush();
+ } catch (IOException e) {
+ throw new IOException(String.format("Upload of %s to %s %s failed after writing %d of %d bytes: %s",
+ localFileName, httpMethod, urlString, bytesWritten, new File(localFileName).length(), e.getMessage()), e);
+ } finally {
+ if (in != null)
+ in.close();
+
+ if (out != null)
+ out.close();
+
+ if (br != null)
+ br.close();
}
- out.flush();
- } finally {
- if (in != null)
- in.close();
-
- if (out != null)
- out.close();
- if (br != null)
- br.close();
+ checkUploadResponse(conn, httpMethod, urlString, localFileName);
+ } finally {
conn.disconnect();
}
}
@@ -444,33 +455,88 @@ public class VmwareContext {
BufferedOutputStream bos = null;
BufferedInputStream is = null;
+ long bytesWrittenThisCall = 0;
try {
- bos = new BufferedOutputStream(conn.getOutputStream());
- is = new BufferedInputStream(new FileInputStream(localFileName));
- int bufferSize = ChunkSize;
- byte[] buffer = new byte[bufferSize];
- while (true) {
- int bytesRead = is.read(buffer, 0, bufferSize);
- if (bytesRead == -1) {
- break;
+ try {
+ bos = new BufferedOutputStream(conn.getOutputStream());
+ is = new BufferedInputStream(new FileInputStream(localFileName));
+ int bufferSize = ChunkSize;
+ byte[] buffer = new byte[bufferSize];
+ while (true) {
+ int bytesRead = is.read(buffer, 0, bufferSize);
+ if (bytesRead == -1) {
+ break;
+ }
+ bos.write(buffer, 0, bytesRead);
+ totalBytesUpdated += bytesRead;
+ bytesWrittenThisCall += bytesRead;
+ bos.flush();
+ if (progressUpdater != null)
+ progressUpdater.action(new Long(totalBytesUpdated));
}
- bos.write(buffer, 0, bytesRead);
- totalBytesUpdated += bytesRead;
bos.flush();
- if (progressUpdater != null)
- progressUpdater.action(new Long(totalBytesUpdated));
+ } catch (IOException e) {
+ throw new IOException(String.format("Upload of %s to %s %s failed after writing %d of %d bytes for this file " +
+ "(%d bytes total written so far for this import): %s",
+ localFileName, httpMethod, urlString, bytesWrittenThisCall, new File(localFileName).length(), totalBytesUpdated, e.getMessage()), e);
+ } finally {
+ if (is != null)
+ is.close();
+ if (bos != null)
+ bos.close();
}
- bos.flush();
- } finally {
- if (is != null)
- is.close();
- if (bos != null)
- bos.close();
+ checkUploadResponse(conn, httpMethod, urlString, localFileName);
+ } finally {
conn.disconnect();
}
}
+ /**
+ * HttpURLConnection does not surface a failed request just because the client finished writing the
+ * request body without an IOException: with chunked transfer encoding many HTTP servers, including
+ * ESXi's NFC endpoint, read and discard the whole body before responding with an error status (e.g. a
+ * VMFS file lock, an out-of-space datastore, or an authentication/session failure). Silently ignoring
+ * the response code is exactly how a rejected/short write can look like a "successful" upload to
+ * CloudStack. Surface the actual status code, reason phrase and (if any) response body so failures like
+ * "file locked" are visible in the CloudStack logs instead of only in vCenter/ESXi's own logs, if at all.
+ */
+ private void checkUploadResponse(HttpURLConnection conn, String httpMethod, String urlString, String localFileName) throws IOException {
+ int responseCode;
+ try {
+ responseCode = conn.getResponseCode();
+ } catch (IOException e) {
+ throw new IOException(String.format("Unable to read the response for %s %s (uploading %s): %s",
+ httpMethod, urlString, localFileName, e.getMessage()), e);
+ }
+
+ if (responseCode < 200 || responseCode >= 300) {
+ String responseBody = readResponseBodyQuietly(conn);
+ boolean hasBody = responseBody != null && !responseBody.trim().isEmpty();
+ throw new IOException(String.format(
+ "%s %s rejected the upload of %s with HTTP %d %s%s",
+ httpMethod, urlString, localFileName, responseCode, conn.getResponseMessage(),
+ hasBody ? (": " + responseBody) : ""));
+ }
+ }
+
+ private String readResponseBodyQuietly(HttpURLConnection conn) {
+ InputStream errorStream = conn.getErrorStream();
+ if (errorStream == null) {
+ return null;
+ }
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream, getCharSetFromConnection(conn)))) {
+ StringBuilder sb = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null && sb.length() < 1024) {
+ sb.append(line);
+ }
+ return sb.toString();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
public long downloadVmdkFile(String urlString, String localFileName, AtomicLong totalBytesDownloaded, ActionDelegate<Long> progressUpdater) throws Exception {
HttpURLConnection conn = getRawHTTPConnection(urlString);