Factorized duplicated code

This commit is contained in:
Matthias Dhellin 2024-10-07 16:57:27 +02:00
parent 1d7ca77a5c
commit 63bdc0ffa2
4 changed files with 114 additions and 151 deletions

View File

@ -39,6 +39,7 @@ import org.apache.cloudstack.backup.backroll.model.BackrollOffering;
import org.apache.cloudstack.backup.backroll.model.BackrollTaskStatus;
import org.apache.cloudstack.backup.backroll.model.BackrollVmBackup;
import org.apache.cloudstack.backup.backroll.model.response.BackrollTaskRequestResponse;
import org.apache.cloudstack.backup.backroll.model.response.StateResponse;
import org.apache.cloudstack.backup.backroll.model.response.api.LoginApiResponse;
import org.apache.cloudstack.backup.backroll.model.response.archive.BackrollArchiveResponse;
import org.apache.cloudstack.backup.backroll.model.response.archive.BackrollBackupsFromVMResponse;
@ -141,8 +142,9 @@ public class BackrollClient {
try {
loginIfAuthenticationFailed();
return this.<BackrollTaskRequestResponse>ok(
post(String.format("/tasks/singlebackup/%s", jobId), null)).location.replace("/api/v1/status/", "");
return this.<BackrollTaskRequestResponse>parse(okBody(
post(String.format("/tasks/singlebackup/%s", jobId), null))).location
.replace("/api/v1/status/", "");
} catch (final Exception e) {
LOG.error("Failed to start Backroll backup job due to: {}", e.getMessage());
}
@ -155,7 +157,8 @@ public class BackrollClient {
loginIfAuthenticationFailed();
try {
return this.<BackrollTaskRequestResponse>ok(get("/backup_policies")).location.replace("/api/v1", "");
return this.<BackrollTaskRequestResponse>parse(okBody(get("/backup_policies"))).location.replace("/api/v1",
"");
} catch (final Exception e) {
LOG.error("Failed to list Backroll jobs due to: {}", e.getMessage());
}
@ -168,10 +171,7 @@ public class BackrollClient {
loginIfAuthenticationFailed();
try {
String bodyStr = waitForGetRequestResponse(idTask);
BackupPoliciesResponse backupPoliciesResponse = new ObjectMapper().readValue(bodyStr,
BackupPoliciesResponse.class);
BackupPoliciesResponse backupPoliciesResponse = waitGet(idTask);
final List<BackupOffering> policies = new ArrayList<>();
for (final BackrollBackupPolicyResponse policy : backupPoliciesResponse.backupPolicies) {
@ -202,18 +202,14 @@ public class BackrollClient {
}
String urlToRequest = this
.<BackrollTaskRequestResponse>ok(post(String.format("/tasks/restore/%s", vmId), jsonBody)).location
.<BackrollTaskRequestResponse>parse(okBody(
post(String.format("/tasks/restore/%s", vmId), jsonBody))).location
.replace("/api/v1", "");
String responseStatus = waitForGetRequestResponse(urlToRequest);
LOG.debug("RESTORE {}", responseStatus);
if (responseStatus.contains("SUCCESS")) {
LOG.debug("RESTORE SUCCESS");
return true;
} else {
return false;
}
} catch (final NotOkException e) {
StateResponse response = waitGet(urlToRequest);
LOG.debug("RESTORE {}", response.state);
return response.state.equals("SUCCESS");
} catch (final NotOkBodyException e) {
return false;
} catch (final IOException | InterruptedException e) {
LOG.error("Ouch! Failed to restore VM with Backroll due to: {}", e.getMessage());
@ -227,34 +223,26 @@ public class BackrollClient {
loginIfAuthenticationFailed();
try {
final HttpResponse backupStatusResponse = get("/status/" + taskId);
BackrollTaskStatus status = new BackrollTaskStatus();
if (isResponseOk(backupStatusResponse)) {
BackrollTaskStatus status = new BackrollTaskStatus();
String body = okBody(get("/status/" + taskId));
HttpEntity body = backupStatusResponse.getEntity();
String bodyStr = EntityUtils.toString(body);
if (bodyStr.contains("FAILURE") || bodyStr.contains("PENDING")) {
BackrollBackupStatusResponse backupStatusRequestResponse = new ObjectMapper().readValue(bodyStr,
BackrollBackupStatusResponse.class);
EntityUtils.consumeQuietly(backupStatusResponse.getEntity());
status.setState(backupStatusRequestResponse.state);
} else {
BackrollBackupStatusSuccessResponse backupStatusSuccessRequestResponse = new ObjectMapper()
.readValue(bodyStr, BackrollBackupStatusSuccessResponse.class);
EntityUtils.consumeQuietly(backupStatusResponse.getEntity());
status.setState(backupStatusSuccessRequestResponse.state);
status.setInfo(backupStatusSuccessRequestResponse.info);
}
return status;
if (body.contains("FAILURE") || body.contains("PENDING")) {
BackrollBackupStatusResponse backupStatusRequestResponse = parse(body);
status.setState(backupStatusRequestResponse.state);
} else {
EntityUtils.consumeQuietly(backupStatusResponse.getEntity());
// throw new CloudRuntimeException("Failed to retrieve backups status for this
// VM via Backroll");
LOG.error("Failed to retrieve backups status for this VM via Backroll");
BackrollBackupStatusSuccessResponse backupStatusSuccessRequestResponse = parse(body);
status.setState(backupStatusSuccessRequestResponse.state);
status.setInfo(backupStatusSuccessRequestResponse.info);
}
return status;
} catch (final NotOkBodyException e) {
// throw new CloudRuntimeException("Failed to retrieve backups status for this
// VM via Backroll");
LOG.error("Failed to retrieve backups status for this VM via Backroll");
} catch (final IOException e) {
LOG.error("Failed to check backups status due to: {}", e.getMessage());
}
@ -267,19 +255,14 @@ public class BackrollClient {
loginIfAuthenticationFailed();
try {
String urlToRequest = this.<BackrollTaskRequestResponse>ok(delete(
String.format("/virtualmachines/%s/backups/%s", vmId, backupName))).location.replace("/api/v1", "");
String urlToRequest = this.<BackrollTaskRequestResponse>parse(okBody(delete(
String.format("/virtualmachines/%s/backups/%s", vmId, backupName)))).location
.replace("/api/v1", "");
String responseStatus = waitForGetRequestResponse(urlToRequest);
LOG.debug(responseStatus);
BackrollBackupsFromVMResponse backrollBackupsFromVMResponse = new ObjectMapper()
.readValue(responseStatus, BackrollBackupsFromVMResponse.class);
if (backrollBackupsFromVMResponse.state.equals("SUCCESS")) {
return true;
} else {
return false;
}
} catch (final NotOkException e) {
BackrollBackupsFromVMResponse backrollBackupsFromVMResponse = waitGet(urlToRequest);
LOG.debug(backrollBackupsFromVMResponse.state);
return backrollBackupsFromVMResponse.state.equals("SUCCESS");
} catch (final NotOkBodyException e) {
return false;
} catch (final Exception e) {
LOG.error("Failed to delete backup using Backroll due to: {}", e.getMessage());
@ -296,18 +279,13 @@ public class BackrollClient {
try {
String urlToRequest = this.<BackrollTaskRequestResponse>ok(
get(String.format("/virtualmachines/%s/repository", vmId))).location.replace("/api/v1", "");
String urlToRequest = this.<BackrollTaskRequestResponse>parse(okBody(
get(String.format("/virtualmachines/%s/repository", vmId)))).location.replace("/api/v1", "");
LOG.debug(urlToRequest);
String bodyStr = waitForGetRequestResponse(urlToRequest);
LOG.debug(bodyStr);
BackrollVmMetricsResponse vmMetricsResponse = new ObjectMapper().readValue(bodyStr,
BackrollVmMetricsResponse.class);
BackrollVmMetricsResponse vmMetricsResponse = waitGet(urlToRequest);
if (vmMetricsResponse != null) {
if (vmMetricsResponse.state.equals("SUCCESS")) {
LOG.debug("SUCCESS ok");
if (vmMetricsResponse.infos != null) {
@ -335,20 +313,16 @@ public class BackrollClient {
try {
String urlToRequest = this.<BackrollTaskRequestResponse>ok(get(
String.format("/virtualmachines/%s/backups/%s", vmId, backupId))).location.replace("/api/v1", "");
String urlToRequest = this.<BackrollTaskRequestResponse>parse(okBody(get(
String.format("/virtualmachines/%s/backups/%s", vmId, backupId)))).location.replace("/api/v1", "");
LOG.debug(urlToRequest);
String bodyStr = waitForGetRequestResponse(urlToRequest);
LOG.debug(bodyStr);
BackrollBackupMetricsResponse metrics = new ObjectMapper().readValue(bodyStr,
BackrollBackupMetricsResponse.class);
BackrollBackupMetricsResponse metrics = waitGet(urlToRequest);
if (metrics.info != null) {
return new BackrollBackupMetrics(Long.parseLong(metrics.info.originalSize),
Long.parseLong(metrics.info.deduplicatedSize));
}
} catch (final NotOkException e) {
} catch (final NotOkBodyException e) {
throw new CloudRuntimeException("Failed to retrieve backups status for this VM via Backroll");
} catch (final IOException | InterruptedException e) {
LOG.error("Failed to check backups status due to: {}", e.getMessage());
@ -364,14 +338,12 @@ public class BackrollClient {
try {
String urlToRequest = this
.<BackrollTaskRequestResponse>ok(get(String.format("/virtualmachines/%s/backups", vmId))).location
.<BackrollTaskRequestResponse>parse(okBody(
get(String.format("/virtualmachines/%s/backups", vmId)))).location
.replace("/api/v1", "");
LOG.debug(urlToRequest);
String bodyStr = waitForGetRequestResponse(urlToRequest);
VirtualMachineBackupsResponse response = new ObjectMapper().readValue(bodyStr,
VirtualMachineBackupsResponse.class);
VirtualMachineBackupsResponse response = waitGet(urlToRequest);
if (response.state.equals("SUCCESS")) {
if (response.info.archives.size() > 0) {
@ -435,71 +407,54 @@ public class BackrollClient {
return response;
}
private boolean isResponseOk(final HttpResponse response) {
if ((response.getStatusLine().getStatusCode() == HttpStatus.SC_OK ||
response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED)) {
return true;
} else {
return false;
}
}
private boolean isResponseAuthorized(final HttpResponse response) {
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK
|| response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED;
}
private class NotOkException extends Exception {
private class NotOkBodyException extends Exception {
}
private <T> T ok(final HttpResponse response)
throws ParseException, IOException, JsonMappingException, JsonProcessingException, NotOkException {
private String okBody(final HttpResponse response)
throws ParseException, IOException, NotOkBodyException {
switch (response.getStatusLine().getStatusCode()) {
case HttpStatus.SC_OK:
case HttpStatus.SC_ACCEPTED:
HttpEntity bodyEntity = response.getEntity();
String bodyString = EntityUtils.toString(bodyEntity); // TODO try and finally close resource?
EntityUtils.consumeQuietly(bodyEntity);
return new ObjectMapper().readValue(bodyString, new TypeReference<T>() {
});
try {
return EntityUtils.toString(bodyEntity);
} finally {
EntityUtils.consumeQuietly(bodyEntity);
}
default:
throw new NotOkException();
throw new NotOkBodyException();
}
}
private String waitForGetRequestResponse(String urlToRequest) throws IOException, InterruptedException {
HttpEntity body;
String bodyStr = "";
int cpt = 0;
private <T> T parse(final String json)
throws JsonMappingException, JsonProcessingException {
return new ObjectMapper().readValue(json, new TypeReference<T>() {
});
}
private <T> T waitGet(String url)
throws IOException, InterruptedException {
// int threshold = 30; // 5 minutes
int threshold = 12; // 2 minutes
int maxAttempts = 12; // 2 minutes
do {
if (cpt == threshold) {
break;
} else {
final HttpResponse response = get(urlToRequest);
LOG.debug("Backroll!!! {}", response);
if (isResponseOk(response)) {
body = response.getEntity();
bodyStr = EntityUtils.toString(body);
LOG.debug(bodyStr);
EntityUtils.consumeQuietly(response.getEntity());
} else {
throw new CloudRuntimeException("An error occured with Backroll");
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
String body = okBody(get(url));
if (!body.contains("PENDING")) {
return parse(body);
}
cpt++;
TimeUnit.SECONDS.sleep(10);
} catch (final NotOkBodyException e) {
throw new CloudRuntimeException("An error occured with Backroll");
}
} while (bodyStr.contains("PENDING"));
if (cpt == threshold) {
bodyStr = "ERROR";
TimeUnit.SECONDS.sleep(10);
}
return bodyStr;
return null;
}
private boolean isAuthenticated() {
@ -536,22 +491,15 @@ public class BackrollClient {
request.setEntity(params);
final HttpResponse response = httpClient.execute(request);
if (isResponseOk(response)) {
HttpEntity body = response.getEntity();
String bodyStr = EntityUtils.toString(body);
LoginApiResponse loginResponse = new ObjectMapper().readValue(bodyStr, LoginApiResponse.class);
try {
LoginApiResponse loginResponse = parse(okBody(response));
backrollToken = loginResponse.accessToken;
LOG.debug("Backroll client - Token : {}", backrollToken);
EntityUtils.consumeQuietly(response.getEntity());
if (StringUtils.isEmpty(loginResponse.accessToken)) {
throw new CloudRuntimeException("Backroll token is not available to perform API requests");
}
} else {
EntityUtils.consumeQuietly(response.getEntity());
} catch (final NotOkBodyException e) {
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_CREATED) {
throw new CloudRuntimeException(
"Failed to create and authenticate Backroll client, please check the settings.");
@ -560,7 +508,6 @@ public class BackrollClient {
"Backroll API call unauthorized, please ask your administrator to fix integration issues.");
}
}
} catch (final IOException e) {
throw new CloudRuntimeException("Failed to authenticate Backroll API service due to:" + e.getMessage());
} catch (JSONException e) {
@ -572,30 +519,20 @@ public class BackrollClient {
private List<BackrollBackup> getBackrollBackups(final String vmId) {
try {
LOG.info("start to list Backroll backups for vm {}", vmId);
final HttpResponse backupsAskingResponse = get("/virtualmachines/" + vmId + "/backups");
String urlToRequest = this
.<BackrollTaskRequestResponse>parse(okBody(get("/virtualmachines/" + vmId + "/backups"))).location
.replace("/api/v1", "");
LOG.debug(urlToRequest);
BackrollBackupsFromVMResponse backrollBackupsFromVMResponse = waitGet(urlToRequest);
if (isResponseOk(backupsAskingResponse)) {
HttpEntity body = backupsAskingResponse.getEntity();
String bodyStr = EntityUtils.toString(body);
BackrollTaskRequestResponse backupTaskRequestResponse = new ObjectMapper().readValue(bodyStr,
BackrollTaskRequestResponse.class);
String urlToRequest = backupTaskRequestResponse.location.replace("/api/v1", "");
LOG.debug(urlToRequest);
EntityUtils.consumeQuietly(backupsAskingResponse.getEntity());
bodyStr = waitForGetRequestResponse(urlToRequest);
LOG.debug(bodyStr);
BackrollBackupsFromVMResponse backrollBackupsFromVMResponse = new ObjectMapper().readValue(bodyStr,
BackrollBackupsFromVMResponse.class);
final List<BackrollBackup> backups = new ArrayList<>();
for (final BackrollArchiveResponse archive : backrollBackupsFromVMResponse.archives.archives) {
backups.add(new BackrollBackup(archive.name));
LOG.debug(archive.name);
}
return backups;
} else {
throw new CloudRuntimeException("Failed to retrieve backups for this VM via Backroll");
final List<BackrollBackup> backups = new ArrayList<>();
for (final BackrollArchiveResponse archive : backrollBackupsFromVMResponse.archives.archives) {
backups.add(new BackrollBackup(archive.name));
LOG.debug(archive.name);
}
return backups;
} catch (final NotOkBodyException e) {
throw new CloudRuntimeException("Failed to retrieve backups for this VM via Backroll");
} catch (final IOException e) {
LOG.error("Failed to list backup form vm with Backroll due to: {}", e);
} catch (InterruptedException e) {

View File

@ -0,0 +1,24 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.backup.backroll.model.response;
import com.fasterxml.jackson.annotation.JsonProperty;
public class StateResponse {
@JsonProperty("state")
public String state;
}

View File

@ -24,6 +24,7 @@ public class BackrollArchiveResponse {
@JsonProperty("archive")
public String archive;
// TODO Merged typo?
@JsonProperty("barchive")
public String barchive;

View File

@ -22,6 +22,7 @@ public class BackupInfos {
@JsonProperty("archive")
public String archive;
// TODO Merged typo?
@JsonProperty("barchive")
public String barchive;