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 4558c581238..da731e295fa 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 @@ -22,15 +22,15 @@ import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.List; -import com.cloud.agent.api.to.VolumeTO; -import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.backup.veeam.VeeamClient; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.log4j.Logger; +import com.cloud.agent.api.to.VolumeTO; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VirtualMachine; public class VeeamBackupProvider extends AdapterBase implements BackupProvider, Configurable { private static final Logger LOG = Logger.getLogger(VeeamBackupProvider.class); @@ -73,7 +73,7 @@ public class VeeamBackupProvider extends AdapterBase implements BackupProvider, public boolean addVMToBackupPolicy(Long zoneId, String policyId, VirtualMachine vm) { String instanceName = vm.getInstanceName(); //TODO: Get vcenter ip - return getClient(zoneId).assignBackupPolicyToVM(policyId, instanceName, ""); + return getClient(zoneId).addVMToVeeamJob(policyId, instanceName, ""); } @Override 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 d80d3f42d1a..44965d955a1 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 @@ -17,6 +17,8 @@ package org.apache.cloudstack.backup.veeam; +import static org.apache.cloudstack.backup.veeam.api.VeeamObjectType.HierarchyRootReference; + import java.io.IOException; import java.net.SocketTimeoutException; import java.net.URI; @@ -35,6 +37,10 @@ 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.HierarchyItem; +import org.apache.cloudstack.backup.veeam.api.HierarchyItems; +import org.apache.cloudstack.backup.veeam.api.ObjectInJob; +import org.apache.cloudstack.backup.veeam.api.ObjectsInJob; import org.apache.cloudstack.backup.veeam.api.Ref; import org.apache.cloudstack.backup.veeam.api.Task; import org.apache.cloudstack.utils.security.SSLUtils; @@ -67,6 +73,7 @@ import org.apache.log4j.Logger; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.nio.TrustAllManager; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator; @@ -138,7 +145,9 @@ public class VeeamClient { LOG.debug("Requested Veeam resource does not exist"); return; } - if (!(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK || response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED) && response.getStatusLine().getStatusCode() != HttpStatus.SC_NO_CONTENT) { + 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."); } } @@ -184,9 +193,86 @@ public class VeeamClient { return response; } - ////////////////////////////////////////////////////////// - //////////////// Public APIs: Backup ///////////////////// - ////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////// + //////////////// Private Veeam Helper Methods ///////////////////// + /////////////////////////////////////////////////////////////////// + + private String findDCHierarchy(final String vmwareDcName) { + LOG.debug("Trying to find hierarchy ID for vmware datacenter: " + vmwareDcName); + + try { + final HttpResponse response = get("/hierarchyRoots"); + checkResponseOK(response); + final ObjectMapper objectMapper = new XmlMapper(); + final EntityReferences references = objectMapper.readValue(response.getEntity().getContent(), EntityReferences.class); + for (final Ref ref : references.getRefs()) { + if (ref.getName().equals(vmwareDcName) && ref.getType().equals(HierarchyRootReference)) { + return ref.getUid(); + } + } + } catch (final IOException e) { + LOG.error("Failed to list Veeam jobs due to:", e); + checkResponseTimeOut(e); + } + throw new CloudRuntimeException("Failed to find hierarchy reference for VMware datacenter " + vmwareDcName + " in Veeam, please ask administrator to check Veeam B&R manager configuration"); + } + + private String lookupVM(final String hierarchyId, final String vmName) { + LOG.debug("Trying to lookup VM from veeam hierarchy:" + hierarchyId + " for vm name:" + vmName); + + try { + final HttpResponse response = get(String.format("/lookup?host=%s&type=Vm&name=%s", hierarchyId, vmName)); + checkResponseOK(response); + final ObjectMapper objectMapper = new XmlMapper(); + final HierarchyItems items = objectMapper.readValue(response.getEntity().getContent(), HierarchyItems.class); + if (items == null || items.getItems() == null || items.getItems().isEmpty()) { + throw new CloudRuntimeException("Could not find VM " + vmName + " in Veeam, please ask administrator to check Veeam B&R manager"); + } + for (final HierarchyItem item : items.getItems()) { + if (item.getObjectName().equals(vmName) && item.getObjectType().equals("Vm")) { + return item.getObjectRef(); + } + } + } catch (final IOException e) { + LOG.error("Failed to list Veeam jobs due to:", e); + checkResponseTimeOut(e); + } + throw new CloudRuntimeException("Failed to lookup VM " + vmName + " in Veeam, please ask administrator to check Veeam B&R manager configuration"); + } + + private Task parseTaskResponse(HttpResponse response) throws IOException { + checkResponseOK(response); + final ObjectMapper objectMapper = new XmlMapper(); + return objectMapper.readValue(response.getEntity().getContent(), Task.class); + } + + private boolean checkTaskStatus(final HttpResponse response) throws IOException { + final Task task = parseTaskResponse(response); + for (int i = 0; i < 20; 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("Operation failed for veeam 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(5000); + } catch (InterruptedException e) { + LOG.debug("Failed to sleep while polling for Veeam task status due to: ", e); + } + } + return false; + } + + //////////////////////////////////////////////////////// + //////////////// Public Veeam APIs ///////////////////// + //////////////////////////////////////////////////////// public List listAllBackups() { LOG.debug("Trying to list Veeam backups"); @@ -208,7 +294,7 @@ public class VeeamClient { } public List listBackupPolicies() { - LOG.debug("Trying to list Veeam jobs that are backup policies"); + LOG.debug("Trying to list backup policies that are Veeam jobs"); try { final HttpResponse response = get("/jobs"); checkResponseOK(response); @@ -226,47 +312,57 @@ 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"); + public boolean startAdhocBackupJob(final String jobId) { + LOG.debug("Trying to start ad-hoc backup for Veeam job: " + jobId); 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); - } - } + final HttpResponse response = post(String.format("/jobs/%s?action=start", jobId), null); + return checkTaskStatus(response); } 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"); + return false; + } + + public boolean addVMToVeeamJob(final String jobId, final String vmwareInstanceName, final String vmwareDcName) { + LOG.debug("Trying to add VM to backup policy that is a veeam job: " + jobId); + try { + final String heirarchyId = findDCHierarchy(vmwareDcName); + final String veeamVmRefId = lookupVM(heirarchyId, vmwareInstanceName); + final CreateObjectInJobSpec vmToBackupJob = new CreateObjectInJobSpec(); + vmToBackupJob.setObjName(vmwareInstanceName); + vmToBackupJob.setObjRef(veeamVmRefId); + final HttpResponse response = post(String.format("/jobs/%s/includes", jobId), vmToBackupJob); + return checkTaskStatus(response); + } catch (final IOException e) { + LOG.error("Failed to add VM to Veeam job due to:", e); + checkResponseTimeOut(e); + } + throw new CloudRuntimeException("Failed to add VM to backup policy likely due to timeout, please check veeam tasks"); + } + + public boolean removeVMFromVeeamJob(final String jobId, final String vmwareInstanceName, final String vmwareDcName) { + LOG.debug("Trying to remove VM from backup policy that is a veeam job: " + jobId); + try { + final String heirarchyId = findDCHierarchy(vmwareDcName); + final String veeamVmRefId = lookupVM(heirarchyId, vmwareInstanceName); + final HttpResponse response = get(String.format("/jobs/%s/includes", jobId)); + checkResponseOK(response); + final ObjectMapper objectMapper = new XmlMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + final ObjectsInJob jobObjects = objectMapper.readValue(response.getEntity().getContent(), ObjectsInJob.class); + for (final ObjectInJob jobObject : jobObjects.getObjects()) { + if (jobObject.getName().equals(vmwareInstanceName) && jobObject.getHierarchyObjRef().equals(veeamVmRefId)) { + final HttpResponse deleteResponse = delete(String.format("/jobs/%s/includes/%s", jobId, jobObject.getObjectInJobId())); + return checkTaskStatus(deleteResponse); + } + } + return checkTaskStatus(response); + } catch (final IOException e) { + LOG.error("Failed to list Veeam jobs due to:", e); + checkResponseTimeOut(e); + } + throw new CloudRuntimeException("Failed to remove VM from backup policy, please check veeam tasks"); } } diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/HierarchyItem.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/HierarchyItem.java new file mode 100644 index 00000000000..46f0e5e420c --- /dev/null +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/HierarchyItem.java @@ -0,0 +1,68 @@ +// 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 = "HierarchyItem") +public class HierarchyItem { + @JacksonXmlProperty(localName = "Type", isAttribute = true) + private String type; + + @JacksonXmlProperty(localName = "ObjectRef") + private String objectRef; + + @JacksonXmlProperty(localName = "ObjectType") + private String objectType; + + @JacksonXmlProperty(localName = "ObjectName") + private String objectName; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getObjectRef() { + return objectRef; + } + + public void setObjectRef(String objectRef) { + this.objectRef = objectRef; + } + + public String getObjectType() { + return objectType; + } + + public void setObjectType(String objectType) { + this.objectType = objectType; + } + + public String getObjectName() { + return objectName; + } + + public void setObjectName(String objectName) { + this.objectName = objectName; + } +} diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/HierarchyItems.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/HierarchyItems.java new file mode 100644 index 00000000000..a87c6b0f983 --- /dev/null +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/HierarchyItems.java @@ -0,0 +1,39 @@ +// 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 = "HierarchyItems") +public class HierarchyItems { + @JacksonXmlProperty(localName = "HierarchyItem") + @JacksonXmlElementWrapper(localName = "HierarchyItem", useWrapping = false) + private List items; + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } +} diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/ObjectInJob.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/ObjectInJob.java new file mode 100644 index 00000000000..987176e59f8 --- /dev/null +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/ObjectInJob.java @@ -0,0 +1,94 @@ +// 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 = "ObjectInJob") +public class ObjectInJob { + @JacksonXmlProperty(localName = "Href", isAttribute = true) + private String href; + + @JacksonXmlProperty(localName = "Type", isAttribute = true) + private String type; + + @JacksonXmlProperty(localName = "Link") + @JacksonXmlElementWrapper(localName = "Links") + private List link; + + @JacksonXmlProperty(localName = "ObjectInJobId", isAttribute = true) + private String objectInJobId; + + @JacksonXmlProperty(localName = "HierarchyObjRef", isAttribute = true) + private String hierarchyObjRef; + + @JacksonXmlProperty(localName = "Name", isAttribute = true) + private String name; + + public String getHref() { + return href; + } + + public void setHref(String href) { + this.href = href; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public List getLink() { + return link; + } + + public void setLink(List link) { + this.link = link; + } + + public String getObjectInJobId() { + return objectInJobId; + } + + public void setObjectInJobId(String objectInJobId) { + this.objectInJobId = objectInJobId; + } + + public String getHierarchyObjRef() { + return hierarchyObjRef; + } + + public void setHierarchyObjRef(String hierarchyObjRef) { + this.hierarchyObjRef = hierarchyObjRef; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/ObjectsInJob.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/ObjectsInJob.java new file mode 100644 index 00000000000..982dae592dc --- /dev/null +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/ObjectsInJob.java @@ -0,0 +1,39 @@ +// 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 = "ObjectsInJob") +public class ObjectsInJob { + @JacksonXmlProperty(localName = "ObjectInJob") + @JacksonXmlElementWrapper(localName = "ObjectInJob", useWrapping = false) + private List objects; + + public List getObjects() { + return objects; + } + + public void setObjects(List objects) { + this.objects = objects; + } +} diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Ref.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Ref.java index b3b8d0b3286..c21c554aebe 100644 --- a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Ref.java +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Ref.java @@ -50,8 +50,7 @@ public class Ref { } public String getUid() { - String[] fields = uid.split(":"); - return fields[fields.length - 1]; + return uid; } public void setUid(String uid) { diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/VeeamObjectType.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/VeeamObjectType.java index 36ebce2646e..82aea34ebf9 100644 --- a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/VeeamObjectType.java +++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/VeeamObjectType.java @@ -18,6 +18,9 @@ package org.apache.cloudstack.backup.veeam.api; public enum VeeamObjectType { + HierarchyRoot, + HierarchyRootReference, + Job, JobReference, 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 19b74707f55..48b0d8de54d 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 @@ -40,8 +40,9 @@ public class VeeamClientTest { } @Test - public void testAssignVMToPolicy() { - client.assignBackupPolicyToVM("8acac50d-3711-4c99-bf7b-76fe9c7e39c3", "i-2-9-VM", "10.2.2.52"); + public void testBackupLifecycle() { + client.addVMToVeeamJob("8acac50d-3711-4c99-bf7b-76fe9c7e39c3", "i-2-9-VM", "10.2.2.52"); + client.startAdhocBackupJob("8acac50d-3711-4c99-bf7b-76fe9c7e39c3"); + client.removeVMFromVeeamJob("8acac50d-3711-4c99-bf7b-76fe9c7e39c3", "i-2-9-VM", "10.2.2.52"); } - } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2e61773623e..7b83f29c5f1 100644 --- a/pom.xml +++ b/pom.xml @@ -92,7 +92,7 @@ 1.10.19 1.6.4 1.11.213 - 2.9.2 + 2.9.5 2.6 3.6 2.6