You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ha...@apache.org on 2022/07/21 06:47:20 UTC

[cloudstack] branch main updated: [Veeam] externalize restore timeout (#6320)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 61e4e862c4 [Veeam] externalize restore timeout (#6320)
61e4e862c4 is described below

commit 61e4e862c4d4828d5e4e5089c9db034fb4d427b9
Author: SadiJr <sa...@gmail.com>
AuthorDate: Thu Jul 21 03:47:13 2022 -0300

    [Veeam] externalize restore timeout (#6320)
    
    * [Veeam] add global timeout configuration for backup restore process
    
    * Use 'this'
    
    * Address reviews
    
    * Address reviews
    
    Co-authored-by: SadiJr <sa...@scclouds.com.br>
---
 .../cloudstack/backup/VeeamBackupProvider.java     |  8 +++--
 .../cloudstack/backup/veeam/VeeamClient.java       | 38 +++++++++++++---------
 .../cloudstack/backup/veeam/VeeamClientTest.java   | 25 +++++++++++++-
 3 files changed, 53 insertions(+), 18 deletions(-)

diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java
index 3781e208d3..072431c4a9 100644
--- a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java
@@ -77,6 +77,9 @@ public class VeeamBackupProvider extends AdapterBase implements BackupProvider,
     private ConfigKey<Integer> VeeamApiRequestTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.request.timeout", "300",
             "The Veeam B&R API request timeout in seconds.", true, ConfigKey.Scope.Zone);
 
+    private static ConfigKey<Integer> VeeamRestoreTimeout = new ConfigKey<>("Advanced", Integer.class, "backup.plugin.veeam.restore.timeout", "600",
+            "The Veeam B&R API restore backup timeout in seconds.", true, ConfigKey.Scope.Zone);
+
     @Inject
     private VmwareDatacenterZoneMapDao vmwareDatacenterZoneMapDao;
     @Inject
@@ -87,7 +90,7 @@ public class VeeamBackupProvider extends AdapterBase implements BackupProvider,
     private VeeamClient getClient(final Long zoneId) {
         try {
             return new VeeamClient(VeeamUrl.valueIn(zoneId), VeeamUsername.valueIn(zoneId), VeeamPassword.valueIn(zoneId),
-                VeeamValidateSSLSecurity.valueIn(zoneId), VeeamApiRequestTimeout.valueIn(zoneId));
+                    VeeamValidateSSLSecurity.valueIn(zoneId), VeeamApiRequestTimeout.valueIn(zoneId), VeeamRestoreTimeout.valueIn(zoneId));
         } catch (URISyntaxException e) {
             throw new CloudRuntimeException("Failed to parse Veeam API URL: " + e.getMessage());
         } catch (NoSuchAlgorithmException | KeyManagementException e) {
@@ -318,7 +321,8 @@ public class VeeamBackupProvider extends AdapterBase implements BackupProvider,
                 VeeamUsername,
                 VeeamPassword,
                 VeeamValidateSSLSecurity,
-                VeeamApiRequestTimeout
+                VeeamApiRequestTimeout,
+                VeeamRestoreTimeout
         };
     }
 
diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamClient.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamClient.java
index e8efc4fb67..2b80ca6654 100644
--- a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamClient.java
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamClient.java
@@ -94,10 +94,13 @@ public class VeeamClient {
     private String veeamServerUsername;
     private String veeamServerPassword;
     private String veeamSessionId = null;
+    private int restoreTimeout;
     private final int veeamServerPort = 22;
 
-    public VeeamClient(final String url, final String username, final String password, final boolean validateCertificate, final int timeout) throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
+    public VeeamClient(final String url, final String username, final String password, final boolean validateCertificate, final int timeout,
+            final int restoreTimeout) throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
         this.apiURI = new URI(url);
+        this.restoreTimeout = restoreTimeout;
 
         final RequestConfig config = RequestConfig.custom()
                 .setConnectTimeout(timeout * 1000)
@@ -173,7 +176,7 @@ public class VeeamClient {
         }
     }
 
-    private HttpResponse get(final String path) throws IOException {
+    protected HttpResponse get(final String path) throws IOException {
         String url = apiURI.toString() + path;
         final HttpGet request = new HttpGet(url);
         request.setHeader(SESSION_HEADER, veeamSessionId);
@@ -274,7 +277,7 @@ public class VeeamClient {
         return objectMapper.readValue(response.getEntity().getContent(), Task.class);
     }
 
-    private RestoreSession parseRestoreSessionResponse(HttpResponse response) throws IOException {
+    protected RestoreSession parseRestoreSessionResponse(HttpResponse response) throws IOException {
         checkResponseOK(response);
         final ObjectMapper objectMapper = new XmlMapper();
         return objectMapper.readValue(response.getEntity().getContent(), RestoreSession.class);
@@ -297,18 +300,7 @@ public class VeeamClient {
                         String type = pair.second();
                         String path = url.replace(apiURI.toString(), "");
                         if (type.equals("RestoreSession")) {
-                            for (int j = 0; j < 120; j++) {
-                                HttpResponse relatedResponse = get(path);
-                                RestoreSession session = parseRestoreSessionResponse(relatedResponse);
-                                if (session.getResult().equals("Success")) {
-                                    return true;
-                                }
-                                try {
-                                    Thread.sleep(5000);
-                                } catch (InterruptedException ignored) {
-                                }
-                            }
-                            throw new CloudRuntimeException("Related job type: " + type + " was not successful");
+                            return checkIfRestoreSessionFinished(type, path);
                         }
                     }
                     return true;
@@ -324,6 +316,22 @@ public class VeeamClient {
         return false;
     }
 
+    protected boolean checkIfRestoreSessionFinished(String type, String path) throws IOException {
+        for (int j = 0; j < this.restoreTimeout; j++) {
+            HttpResponse relatedResponse = get(path);
+            RestoreSession session = parseRestoreSessionResponse(relatedResponse);
+            if (session.getResult().equals("Success")) {
+                return true;
+            }
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException ignored) {
+                LOG.trace(String.format("Ignoring InterruptedException [%s] when waiting for restore session finishes.", ignored.getMessage()));
+            }
+        }
+        throw new CloudRuntimeException("Related job type: " + type + " was not successful");
+    }
+
     private Pair<String, String> getRelatedLinkPair(List<Link> links) {
         for (Link link : links) {
             if (link.getRel().equals("Related")) {
diff --git a/plugins/backup/veeam/src/test/java/org/apache/cloudstack/backup/veeam/VeeamClientTest.java b/plugins/backup/veeam/src/test/java/org/apache/cloudstack/backup/veeam/VeeamClientTest.java
index 7269abb249..7733384e4b 100644
--- a/plugins/backup/veeam/src/test/java/org/apache/cloudstack/backup/veeam/VeeamClientTest.java
+++ b/plugins/backup/veeam/src/test/java/org/apache/cloudstack/backup/veeam/VeeamClientTest.java
@@ -25,15 +25,20 @@ import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching;
 import static com.github.tomakehurst.wiremock.client.WireMock.verify;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.times;
 
+import java.io.IOException;
 import java.util.List;
 
 import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.backup.veeam.api.RestoreSession;
+import org.apache.http.HttpResponse;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mockito;
+import org.springframework.test.util.ReflectionTestUtils;
 
 import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
@@ -57,7 +62,7 @@ public class VeeamClientTest {
                         .withStatus(201)
                         .withHeader("X-RestSvcSessionId", "some-session-auth-id")
                         .withBody("")));
-        client = new VeeamClient("http://localhost:9399/api/", adminUsername, adminPassword, true, 60);
+        client = new VeeamClient("http://localhost:9399/api/", adminUsername, adminPassword, true, 60, 600);
         mockClient = Mockito.mock(VeeamClient.class);
         Mockito.when(mockClient.getRepositoryNameFromJob(Mockito.anyString())).thenCallRealMethod();
     }
@@ -139,4 +144,22 @@ public class VeeamClientTest {
         String repositoryNameFromJob = mockClient.getRepositoryNameFromJob(backupName);
         Assert.assertEquals("test", repositoryNameFromJob);
     }
+
+    @Test
+    public void checkIfRestoreSessionFinishedTestTimeoutException() throws IOException {
+        try {
+            ReflectionTestUtils.setField(mockClient, "restoreTimeout", 10);
+            RestoreSession restoreSession = Mockito.mock(RestoreSession.class);
+            HttpResponse httpResponse = Mockito.mock(HttpResponse.class);
+            Mockito.when(mockClient.get(Mockito.anyString())).thenReturn(httpResponse);
+            Mockito.when(mockClient.parseRestoreSessionResponse(httpResponse)).thenReturn(restoreSession);
+            Mockito.when(restoreSession.getResult()).thenReturn("No Success");
+            Mockito.when(mockClient.checkIfRestoreSessionFinished(Mockito.eq("RestoreTest"), Mockito.eq("any"))).thenCallRealMethod();
+            mockClient.checkIfRestoreSessionFinished("RestoreTest", "any");
+            fail();
+        } catch (Exception e) {
+            Assert.assertEquals("Related job type: RestoreTest was not successful", e.getMessage());
+        }
+        Mockito.verify(mockClient, times(10)).get(Mockito.anyString());
+    }
 }
\ No newline at end of file