Merge branch 'bckuprecframework' of github.com:shapeblue/cloudstack into bckuprecframework
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 e7bc1c3..e664edd 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.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 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 d80d3f4..44965d9 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.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 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 @@
             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 @@
         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<VeeamBackup> listAllBackups() {
         LOG.debug("Trying to list Veeam backups");
@@ -208,7 +294,7 @@
     }
 
     public List<BackupPolicy> 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 @@
         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 0000000..46f0e5e
--- /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 0000000..a87c6b0
--- /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<HierarchyItem> items;
+
+    public List<HierarchyItem> getItems() {
+        return items;
+    }
+
+    public void setItems(List<HierarchyItem> 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 0000000..987176e
--- /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> 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<Link> getLink() {
+        return link;
+    }
+
+    public void setLink(List<Link> 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 0000000..982dae5
--- /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<ObjectInJob> objects;
+
+    public List<ObjectInJob> getObjects() {
+        return objects;
+    }
+
+    public void setObjects(List<ObjectInJob> 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 b3b8d0b..c21c554 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 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 36ebce2..82aea34 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 19b7470..48b0d8d 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 @@
     }
 
     @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 2e61773..7b83f29 100644
--- a/pom.xml
+++ b/pom.xml
@@ -92,7 +92,7 @@
     <cs.mockito.version>1.10.19</cs.mockito.version>
     <cs.powermock.version>1.6.4</cs.powermock.version>
     <cs.aws.sdk.version>1.11.213</cs.aws.sdk.version>
-    <cs.jackson.version>2.9.2</cs.jackson.version>
+    <cs.jackson.version>2.9.5</cs.jackson.version>
     <cs.lang.version>2.6</cs.lang.version>
     <cs.commons-lang3.version>3.6</cs.commons-lang3.version>
     <cs.commons-io.version>2.6</cs.commons-io.version>