wip new method to allow assigning and checking of a VM to a Veeam job

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2018-05-30 14:01:09 +05:30
parent 01059d9d5f
commit 400f1412fd
5 changed files with 278 additions and 4 deletions

View File

@ -33,9 +33,12 @@ import javax.net.ssl.X509TrustManager;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.backup.BackupPolicy;
import org.apache.cloudstack.backup.veeam.api.CreateObjectInJobSpec;
import org.apache.cloudstack.backup.veeam.api.EntityReferences;
import org.apache.cloudstack.backup.veeam.api.Ref;
import org.apache.cloudstack.backup.veeam.api.Task;
import org.apache.cloudstack.utils.security.SSLUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
@ -47,12 +50,14 @@ import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCookieStore;
@ -64,6 +69,7 @@ import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.nio.TrustAllManager;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
public class VeeamClient {
private static final Logger LOG = Logger.getLogger(VeeamClient.class);
@ -132,8 +138,8 @@ public class VeeamClient {
LOG.debug("Requested Veeam resource does not exist");
return;
}
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK && response.getStatusLine().getStatusCode() != HttpStatus.SC_NO_CONTENT) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to find the requested resource and get valid response from Veeam B&R API call, please ask your administrator to diagnose and fix issues.");
if (!(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK || response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED) && response.getStatusLine().getStatusCode() != HttpStatus.SC_NO_CONTENT) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to get valid response from Veeam B&R API call, please ask your administrator to diagnose and fix issues.");
}
}
@ -150,13 +156,34 @@ public class VeeamClient {
return response;
}
private HttpResponse post(final String path, final Object item) throws IOException {
private HttpResponse post(final String path, final Object obj) throws IOException {
String xml = null;
if (obj != null) {
XmlMapper xmlMapper = new XmlMapper();
xml = xmlMapper.writer()
.with(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
.writeValueAsString(obj);
// Remove invalid/empty xmlns
xml = xml.replace(" xmlns=\"\"", "");
}
final HttpPost request = new HttpPost(apiURI.toString() + path);
request.setHeader("Content-type", "application/xml");
if (StringUtils.isNotBlank(xml)) {
request.setEntity(new StringEntity(xml));
}
final HttpResponse response = httpClient.execute(request, httpContext);
checkAuthFailure(response);
return response;
}
private HttpResponse delete(final String path) throws IOException {
final HttpResponse response = httpClient.execute(new HttpDelete(apiURI.toString() + path), httpContext);
checkAuthFailure(response);
return response;
}
//////////////////////////////////////////////////////////
//////////////// Public APIs: Backup /////////////////////
//////////////////////////////////////////////////////////
@ -180,7 +207,6 @@ public class VeeamClient {
return new ArrayList<>();
}
public List<BackupPolicy> listBackupPolicies() {
LOG.debug("Trying to list Veeam jobs that are backup policies");
try {
@ -200,4 +226,47 @@ public class VeeamClient {
return new ArrayList<>();
}
private Task parseTaskResponse(HttpResponse response) throws IOException {
checkResponseOK(response);
final ObjectMapper objectMapper = new XmlMapper();
return objectMapper.readValue(response.getEntity().getContent(), Task.class);
}
// FIXME: pass an object that implement a common interface across backup plugins
public boolean assignBackupPolicyToVM(final String jobId, final String vmwareInstanceName, final String vcIpOrName) {
LOG.debug("Trying to assign VM to backup policy that is a veeam job");
try {
//FIXME: add logic to find hierarical root based on vCenter details this is useful in env with multiple VCs
final String heirarchyId = "dec37163-39df-4c4b-9690-899cf5543bf6";
final CreateObjectInJobSpec vmToBackupJob = new CreateObjectInJobSpec();
vmToBackupJob.setObjName(vmwareInstanceName);
vmToBackupJob.setObjRef(String.format("urn:VMware:VM:%s.%s", heirarchyId, vmwareInstanceName));
final HttpResponse response = post(String.format("/jobs/%s/includes", jobId), vmToBackupJob);
final Task task = parseTaskResponse(response);
for (int i = 0; i < 5; i++) {
final HttpResponse taskResponse = get("/tasks/" + task.getTaskId());
final Task polledTask = parseTaskResponse(taskResponse);
if (polledTask.getState().equals("Finished")) {
final HttpResponse taskDeleteResponse = delete("/tasks/" + task.getTaskId());
if (taskDeleteResponse.getStatusLine().getStatusCode() != HttpStatus.SC_NO_CONTENT) {
LOG.warn("Failed to cleanup VM assign task on veeam job for task id=" + task.getTaskId());
}
if (polledTask.getResult().getSuccess().equals("true")) {
return true;
}
throw new CloudRuntimeException("Failed to assign VM to backup policy due to: " + polledTask.getResult().getMessage());
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
LOG.debug("Failed to sleep while polling for task status due to: ", e);
}
}
} catch (final IOException e) {
LOG.error("Failed to list Veeam jobs due to:", e);
checkResponseTimeOut(e);
}
throw new CloudRuntimeException("Failed to assign VM to backup policy likely due to timeout, please check veeam tasks");
}
}

View File

@ -0,0 +1,46 @@
// 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.veeam.api;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JacksonXmlRootElement(localName = "CreateObjectInJobSpec", namespace = "http://www.veeam.com/ent/v1.0")
public class CreateObjectInJobSpec {
@JacksonXmlProperty(localName = "HierarchyObjRef")
String objRef;
@JacksonXmlProperty(localName = "HierarchyObjName")
String objName;
public String getObjRef() {
return objRef;
}
public void setObjRef(String objRef) {
this.objRef = objRef;
}
public String getObjName() {
return objName;
}
public void setObjName(String objName) {
this.objName = objName;
}
}

View File

@ -0,0 +1,47 @@
// 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.veeam.api;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JacksonXmlRootElement(localName = "Result")
public class Result {
@JacksonXmlProperty(localName = "Success", isAttribute = true)
private String success;
@JacksonXmlProperty(localName = "Message")
private String message;
public String getSuccess() {
return success;
}
public void setSuccess(String success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -0,0 +1,106 @@
// 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.veeam.api;
import java.util.List;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JacksonXmlRootElement(localName = "CreateObjectInJobSpec")
public class Task {
@JacksonXmlProperty(localName = "Type", isAttribute = true)
private String type;
@JacksonXmlProperty(localName = "Href", isAttribute = true)
private String href;
@JacksonXmlProperty(localName = "Link")
@JacksonXmlElementWrapper(localName = "Links")
private List<Link> link;
@JacksonXmlProperty(localName = "TaskId")
private String taskId;
@JacksonXmlProperty(localName = "State")
private String state;
@JacksonXmlProperty(localName = "Operation")
private String operation;
@JacksonXmlProperty(localName = "Result")
@JacksonXmlElementWrapper(localName = "Result", useWrapping = false)
private Result result;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getHref() {
return href;
}
public void setHref(String href) {
this.href = href;
}
public List<Link> getLink() {
return link;
}
public void setLink(List<Link> link) {
this.link = link;
}
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getOperation() {
return operation;
}
public void setOperation(String operation) {
this.operation = operation;
}
public Result getResult() {
return result;
}
public void setResult(Result result) {
this.result = result;
}
}

View File

@ -38,4 +38,10 @@ public class VeeamClientTest {
public void testPolicies() {
client.listBackupPolicies();
}
@Test
public void testAssignVMToPolicy() {
client.assignBackupPolicyToVM("8acac50d-3711-4c99-bf7b-76fe9c7e39c3", "i-2-9-VM", "10.2.2.52");
}
}