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

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


The following commit(s) were added to refs/heads/4.20 by this push:
     new 18c4bdf63a4 vmware-base: fix template copy from secondary to primary 
(#13977)
18c4bdf63a4 is described below

commit 18c4bdf63a4e2e0bb37c209eea11e2c8756a681d
Author: Wei Zhou <[email protected]>
AuthorDate: Thu Nov 5 20:33:54 2026 +0100

    vmware-base: fix template copy from secondary to primary (#13977)
    
    * vmware-base: fix template copy from secondary to primary
    
    * vmware-base: refactoring
    
    * test: revert curl commands to wget in test_routers_network_ops (PR 13977)
    
    Revert test_routers_network_ops.py back to wget commands after fixing 
underlying
    macchina VM file system corruption issue.
    
    Context:
    - Previous commit changed wget to curl due to libpcre.so.1 library error in 
macchina VMs
    - Error was: 'wget: /usr/lib/libpcre.so.1 is not an ELF file'
    - Root cause: file system corruption during template copy from secondary to 
primary storage
    - Fix: PR 13977 (vmware-base: fix template copy from secondary to primary) 
resolves the issue
    - This revert verifies wget is working correctly again after the underlying 
fix
    
    Changes:
    - Replace 'curl -v -m 1 -o index.html -sL www.google.com' back to 'wget -t 
1 -T <timeout> www.google.com'
    - Affects TestRedundantIsolateNetworks and TestIsolatedNetworks test classes
    - Apply timeout values of 5 or 1 second depending on test context
---
 test/integration/smoke/test_routers_network_ops.py |  16 +--
 .../hypervisor/vmware/util/VmwareContext.java      | 109 +++++++++++++++------
 2 files changed, 88 insertions(+), 37 deletions(-)

diff --git a/test/integration/smoke/test_routers_network_ops.py 
b/test/integration/smoke/test_routers_network_ops.py
index ac51d3736ad..83fc9edf1d0 100644
--- a/test/integration/smoke/test_routers_network_ops.py
+++ b/test/integration/smoke/test_routers_network_ops.py
@@ -287,7 +287,7 @@ class TestRedundantIsolateNetworks(cloudstackTestCase):
                          )
 
         expected = 1
-        ssh_command = "curl -v -m 1 -o index.html -sL www.google.com"
+        ssh_command = "wget -t 1 -T 5 www.google.com"
         check_string = "200 OK"
         result = check_router_command(virtual_machine, nat_rule.ipaddress, 
ssh_command, check_string, self)
 
@@ -307,7 +307,7 @@ class TestRedundantIsolateNetworks(cloudstackTestCase):
                                  )
 
         expected = 0
-        ssh_command = "curl -v -m 1 -o index.html -sL www.google.com"
+        ssh_command = "wget -t 1 -T 1 www.google.com"
         check_string = "200 OK"
         result = check_router_command(virtual_machine, nat_rule.ipaddress, 
ssh_command, check_string, self)
 
@@ -451,7 +451,7 @@ class TestRedundantIsolateNetworks(cloudstackTestCase):
                          )
 
         expected = 0
-        ssh_command = "curl -v -m 1 -o index.html -sL www.google.com"
+        ssh_command = "wget -t 1 -T 1 www.google.com"
         check_string = "200 OK"
         result = check_router_command(virtual_machine, nat_rule.ipaddress, 
ssh_command, check_string, self)
 
@@ -480,7 +480,7 @@ class TestRedundantIsolateNetworks(cloudstackTestCase):
                                  )
 
         expected = 1
-        ssh_command = "curl -v -m 1 -o index.html -sL www.google.com"
+        ssh_command = "wget -t 1 -T 5 www.google.com"
         check_string = "200 OK"
         result = check_router_command(virtual_machine, nat_rule.ipaddress, 
ssh_command, check_string, self)
 
@@ -840,7 +840,7 @@ class TestIsolatedNetworks(cloudstackTestCase):
                          )
 
         expected = 1
-        ssh_command = "curl -v -m 1 -o index.html -sL www.google.com"
+        ssh_command = "wget -t 1 -T 5 www.google.com"
         check_string = "200 OK"
         result = check_router_command(virtual_machine, nat_rule.ipaddress, 
ssh_command, check_string, self)
 
@@ -860,7 +860,7 @@ class TestIsolatedNetworks(cloudstackTestCase):
                                  )
 
         expected = 0
-        ssh_command = "curl -v -m 1 -o index.html -sL www.google.com"
+        ssh_command = "wget -t 1 -T 1 www.google.com"
         check_string = "200 OK"
         result = check_router_command(virtual_machine, nat_rule.ipaddress, 
ssh_command, check_string, self)
 
@@ -995,7 +995,7 @@ class TestIsolatedNetworks(cloudstackTestCase):
                          )
 
         expected = 0
-        ssh_command = "curl -v -m 1 -o index.html -sL www.google.com"
+        ssh_command = "wget -t 1 -T 1 www.google.com"
         check_string = "200 OK"
         result = check_router_command(virtual_machine, nat_rule.ipaddress, 
ssh_command, check_string, self)
 
@@ -1015,7 +1015,7 @@ class TestIsolatedNetworks(cloudstackTestCase):
                                  )
 
         expected = 1
-        ssh_command = "curl -v -m 1 -o index.html -sL www.google.com"
+        ssh_command = "wget -t 1 -T 5 www.google.com"
         check_string = "200 OK"
         result = check_router_command(virtual_machine, nat_rule.ipaddress, 
ssh_command, check_string, self)
 
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..3b7c0627fe3 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;
@@ -384,29 +385,29 @@ public class VmwareContext {
         conn.setRequestProperty("content-type", contentType);
         conn.setRequestProperty("content-length", Long.toString(new 
File(localFileName).length()));
         connectWithRetry(conn);
-        OutputStream out = null;
-        InputStream in = null;
-        BufferedReader br = null;
 
         try {
-            out = conn.getOutputStream();
-            in = new FileInputStream(localFileName);
+            writeFileToConnection(conn, httpMethod, urlString, localFileName);
+            checkUploadResponse(conn, httpMethod, urlString, localFileName);
+        } finally {
+            conn.disconnect();
+        }
+    }
+
+    private void writeFileToConnection(HttpURLConnection conn, String 
httpMethod, String urlString, String localFileName) throws IOException {
+        long bytesWritten = 0;
+        try (OutputStream out = conn.getOutputStream();
+             InputStream in = new FileInputStream(localFileName)) {
             byte[] buf = new byte[ChunkSize];
-            int len = 0;
+            int len;
             while ((len = in.read(buf)) > 0) {
                 out.write(buf, 0, len);
+                bytesWritten += len;
             }
             out.flush();
-        } finally {
-            if (in != null)
-                in.close();
-
-            if (out != null)
-                out.close();
-
-            if (br != null)
-                br.close();
-            conn.disconnect();
+        } 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);
         }
     }
 
@@ -442,32 +443,82 @@ public class VmwareContext {
         conn.setRequestProperty("content-length", Long.toString(new 
File(localFileName).length()));
         connectWithRetry(conn);
 
-        BufferedOutputStream bos = null;
-        BufferedInputStream is = null;
         try {
-            bos = new BufferedOutputStream(conn.getOutputStream());
-            is = new BufferedInputStream(new FileInputStream(localFileName));
-            int bufferSize = ChunkSize;
-            byte[] buffer = new byte[bufferSize];
+            writeVmdkFileToConnection(conn, httpMethod, urlString, 
localFileName, totalBytesUpdated, progressUpdater);
+            checkUploadResponse(conn, httpMethod, urlString, localFileName);
+        } finally {
+            conn.disconnect();
+        }
+    }
+
+    private void writeVmdkFileToConnection(HttpURLConnection conn, String 
httpMethod, String urlString, String localFileName, long totalBytesUpdated,
+            ActionDelegate<Long> progressUpdater) throws IOException {
+        long bytesWrittenThisCall = 0;
+        try (BufferedOutputStream bos = new 
BufferedOutputStream(conn.getOutputStream());
+             BufferedInputStream is = new BufferedInputStream(new 
FileInputStream(localFileName))) {
+            byte[] buffer = new byte[ChunkSize];
             while (true) {
-                int bytesRead = is.read(buffer, 0, bufferSize);
+                int bytesRead = is.read(buffer, 0, ChunkSize);
                 if (bytesRead == -1) {
                     break;
                 }
                 bos.write(buffer, 0, bytesRead);
                 totalBytesUpdated += bytesRead;
+                bytesWrittenThisCall += bytesRead;
                 bos.flush();
                 if (progressUpdater != null)
                     progressUpdater.action(new Long(totalBytesUpdated));
             }
             bos.flush();
-        } finally {
-            if (is != null)
-                is.close();
-            if (bos != null)
-                bos.close();
+        } 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);
+        }
+    }
 
-            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;
         }
     }
 

Reply via email to