You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2020/03/03 12:28:21 UTC

[cloudstack] branch master updated: CloudStack Backup & Recovery Framework (#3553)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 318924d  CloudStack Backup & Recovery Framework (#3553)
318924d is described below

commit 318924d801a6bb0f7d3f77e1a5a432267a3db359
Author: Rohit Yadav <ro...@shapeblue.com>
AuthorDate: Tue Mar 3 17:57:58 2020 +0530

    CloudStack Backup & Recovery Framework (#3553)
---
 .travis.yml                                        |    1 +
 api/src/main/java/com/cloud/event/EventTypes.java  |   11 +
 .../java/com/cloud/hypervisor/HypervisorGuru.java  |    8 +-
 .../main/java/com/cloud/server/ResourceTag.java    |    1 +
 api/src/main/java/com/cloud/vm/VirtualMachine.java |   23 +-
 .../apache/cloudstack/api/ApiCommandJobType.java   |    1 +
 .../org/apache/cloudstack/api/ApiConstants.java    |    9 +-
 .../apache/cloudstack/api/BaseBackupListCmd.java   |   49 +
 .../apache/cloudstack/api/ResponseGenerator.java   |   16 +-
 .../admin/backup/DeleteBackupOfferingCmd.java      |   92 ++
 .../admin/backup/ImportBackupOfferingCmd.java      |  145 +++
 .../backup/ListBackupProviderOfferingsCmd.java     |   94 ++
 .../admin/backup/ListBackupProvidersCmd.java       |   98 ++
 .../AssignVirtualMachineToBackupOfferingCmd.java   |  122 +++
 .../api/command/user/backup/CreateBackupCmd.java   |  126 +++
 .../user/backup/CreateBackupScheduleCmd.java       |  128 +++
 .../api/command/user/backup/DeleteBackupCmd.java   |  111 ++
 .../user/backup/DeleteBackupScheduleCmd.java       |   99 ++
 .../user/backup/ListBackupOfferingsCmd.java        |   96 ++
 .../command/user/backup/ListBackupScheduleCmd.java |  100 ++
 .../api/command/user/backup/ListBackupsCmd.java    |  135 +++
 .../RemoveVirtualMachineFromBackupOfferingCmd.java |  118 +++
 .../api/command/user/backup/RestoreBackupCmd.java  |  111 ++
 .../RestoreVolumeFromBackupAndAttachToVMCmd.java   |  133 +++
 .../user/backup/UpdateBackupScheduleCmd.java       |   24 +-
 .../api/response/BackupOfferingResponse.java       |   95 ++
 .../api/response/BackupProviderResponse.java       |   53 +
 .../cloudstack/api/response/BackupResponse.java    |  246 +++++
 .../api/response/BackupRestorePointResponse.java   |   66 ++
 .../api/response/BackupScheduleResponse.java       |   91 ++
 .../cloudstack/api/response/UserVmResponse.java    |   24 +
 .../java/org/apache/cloudstack/backup/Backup.java  |  142 +++
 .../apache/cloudstack/backup/BackupManager.java    |  140 +++
 .../apache/cloudstack/backup/BackupOffering.java   |   32 +
 .../apache/cloudstack/backup/BackupProvider.java   |  111 ++
 .../apache/cloudstack/backup/BackupSchedule.java   |   25 +-
 .../apache/cloudstack/backup/BackupService.java    |   37 +
 .../org/apache/cloudstack/usage/UsageTypes.java    |    2 +
 client/pom.xml                                     |   53 +-
 .../META-INF/cloudstack/backup/module.properties   |   21 +
 ...g-core-lifecycle-backup-context-inheritable.xml |   32 +
 .../core/spring-core-registry-core-context.xml     |    4 +
 .../java/com/cloud/network/dao/NetworkDao.java     |    2 +
 .../java/com/cloud/network/dao/NetworkDaoImpl.java |    8 +
 .../com/cloud/service/dao/ServiceOfferingDao.java  |    2 +
 .../cloud/service/dao/ServiceOfferingDaoImpl.java  |   16 +
 .../com/cloud/storage/dao/DiskOfferingDao.java     |    3 +
 .../com/cloud/storage/dao/DiskOfferingDaoImpl.java |   41 +
 .../com/cloud/storage/dao/VMTemplatePoolDao.java   |    4 +
 .../cloud/storage/dao/VMTemplatePoolDaoImpl.java   |   24 +
 .../main/java/com/cloud/storage/dao/VolumeDao.java |    2 +
 .../java/com/cloud/storage/dao/VolumeDaoImpl.java  |   10 +
 .../main/java/com/cloud/usage/UsageBackupVO.java   |  172 ++++
 .../dao/UsageBackupDao.java}                       |   23 +-
 .../com/cloud/usage/dao/UsageBackupDaoImpl.java    |  139 +++
 .../src/main/java/com/cloud/vm/VMInstanceVO.java   |   81 +-
 .../src/main/java/com/cloud/vm/dao/NicDao.java     |    2 +
 .../src/main/java/com/cloud/vm/dao/NicDaoImpl.java |    8 +
 .../main/java/com/cloud/vm/dao/VMInstanceDao.java  |    4 +
 .../java/com/cloud/vm/dao/VMInstanceDaoImpl.java   |   24 +
 .../apache/cloudstack/backup/BackupOfferingVO.java |  126 +++
 .../apache/cloudstack/backup/BackupScheduleVO.java |  124 +++
 .../org/apache/cloudstack/backup/BackupVO.java     |  190 ++++
 .../apache/cloudstack/backup/dao/BackupDao.java}   |   24 +-
 .../cloudstack/backup/dao/BackupDaoImpl.java       |  172 ++++
 .../cloudstack/backup/dao/BackupOfferingDao.java}  |   22 +-
 .../backup/dao/BackupOfferingDaoImpl.java          |   88 ++
 .../cloudstack/backup/dao/BackupScheduleDao.java}  |   21 +-
 .../backup/dao/BackupScheduleDaoImpl.java          |   86 ++
 .../engine/cloud/entity/api/db/VMEntityVO.java     |   53 +-
 .../spring-engine-schema-core-daos-context.xml     |    4 +
 .../resources/META-INF/db/schema-41300to41400.sql  |  250 +++++
 .../cloud/storage/dao/DiskOfferingDaoImplTest.java |   56 ++
 .../main/java/com/cloud/utils/db/GenericDao.java   |    2 +
 .../java/com/cloud/utils/db/GenericDaoBase.java    |   28 +
 .../apache/cloudstack/quota/QuotaManagerImpl.java  |    1 +
 .../cloudstack/quota/constant/QuotaTypes.java      |    5 +-
 plugins/backup/dummy/pom.xml                       |   41 +
 .../cloudstack/backup/DummyBackupProvider.java     |  134 +++
 .../cloudstack/dummy-backup/module.properties      |   18 +
 .../dummy-backup/spring-backup-dummy-context.xml   |   27 +
 plugins/backup/veeam/pom.xml                       |   54 +
 .../cloudstack/backup/VeeamBackupProvider.java     |  310 ++++++
 .../backup/veeam/VeeamBackupOffering.java          |   78 ++
 .../cloudstack/backup/veeam/VeeamClient.java       |  654 ++++++++++++
 .../cloudstack/backup/veeam/VeeamObject.java       |   22 +-
 .../backup/veeam/api/BackupJobCloneInfo.java       |   58 ++
 .../backup/veeam/api/CreateObjectInJobSpec.java    |   31 +-
 .../backup/veeam/api/EntityReferences.java         |   27 +-
 .../cloudstack/backup/veeam/api/HierarchyItem.java |   68 ++
 .../backup/veeam/api/HierarchyItems.java           |   30 +-
 .../apache/cloudstack/backup/veeam/api/Job.java    |  163 +++
 .../cloudstack/backup/veeam/api/JobCloneSpec.java  |   41 +
 .../apache/cloudstack/backup/veeam/api/Link.java   |   69 ++
 .../cloudstack/backup/veeam/api/ObjectInJob.java   |   94 ++
 .../cloudstack/backup/veeam/api/ObjectsInJob.java  |   30 +-
 .../apache/cloudstack/backup/veeam/api/Ref.java    |   83 ++
 .../backup/veeam/api/RestoreSession.java           |  120 +++
 .../apache/cloudstack/backup/veeam/api/Result.java |   32 +-
 .../apache/cloudstack/backup/veeam/api/Task.java   |  106 ++
 .../META-INF/cloudstack/veeam/module.properties    |   18 +
 .../veeam/spring-backup-veeam-context.xml          |   27 +
 .../cloudstack/backup/veeam/VeeamClientTest.java   |   85 ++
 .../java/com/cloud/simulator/SimulatorGuru.java    |   38 +
 .../java/com/cloud/hypervisor/guru/VMwareGuru.java |  759 +++++++++++++-
 .../vmware/manager/VmwareManagerImpl.java          |    4 +-
 plugins/pom.xml                                    |   15 +-
 pom.xml                                            |    5 +-
 server/src/main/java/com/cloud/api/ApiDBUtils.java |   33 +
 .../main/java/com/cloud/api/ApiResponseHelper.java |   47 +
 .../com/cloud/api/query/dao/UserVmJoinDaoImpl.java |    6 +-
 .../java/com/cloud/api/query/vo/UserVmJoinVO.java  |   17 +
 .../com/cloud/hypervisor/HypervisorGuruBase.java   |   12 +
 .../com/cloud/storage/VolumeApiServiceImpl.java    |    9 +
 .../java/com/cloud/usage/UsageServiceImpl.java     |   43 +-
 .../cloudstack/backup/BackupManagerImpl.java       | 1063 ++++++++++++++++++++
 .../core/spring-server-core-managers-context.xml   |    5 +
 .../cloud/storage/VolumeApiServiceImplTest.java    |    3 +
 .../java/com/cloud/user/MockUsageEventDao.java     |    5 +
 .../java/com/cloud/vpc/dao/MockNetworkDaoImpl.java |    5 +
 .../smoke/test_backup_recovery_dummy.py            |  152 +++
 tools/apidoc/gen_toc.py                            |    2 +
 tools/marvin/marvin/lib/base.py                    |  103 ++
 ui/css/cloudstack3.css                             |   26 +-
 ui/index.html                                      |    1 +
 ui/l10n/en.js                                      |   14 +
 ui/scripts/configuration.js                        |  273 ++++-
 ui/scripts/instances.js                            |  429 ++++++++
 ui/scripts/storage.js                              |  340 +++++++
 ui/scripts/ui-custom/backupSchedule.js             |  181 ++++
 .../java/com/cloud/usage/UsageManagerImpl.java     |   35 +-
 .../com/cloud/usage/parser/BackupUsageParser.java  |  132 +++
 utils/src/main/java/com/cloud/utils/UuidUtils.java |   22 +
 .../cloud/hypervisor/vmware/mo/DatastoreMO.java    |    2 +-
 .../com/cloud/hypervisor/vmware/mo/NetworkMO.java  |    9 +
 .../cloud/hypervisor/vmware/util/VmwareClient.java |    2 +-
 136 files changed, 10790 insertions(+), 264 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index e5ad914..18564f9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -46,6 +46,7 @@ env:
              smoke/test_affinity_groups
              smoke/test_affinity_groups_projects
              smoke/test_async_job
+             smoke/test_backup_recovery_dummy
              smoke/test_create_list_domain_account_project
              smoke/test_create_network
              smoke/test_deploy_vgpu_enabled_vm
diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java
index 33950f8..c74e9b7 100644
--- a/api/src/main/java/com/cloud/event/EventTypes.java
+++ b/api/src/main/java/com/cloud/event/EventTypes.java
@@ -478,6 +478,17 @@ public class EventTypes {
     public static final String EVENT_VM_SNAPSHOT_OFF_PRIMARY = "VMSNAPSHOT.OFF_PRIMARY";
     public static final String EVENT_VM_SNAPSHOT_REVERT = "VMSNAPSHOT.REVERTTO";
 
+    // Backup and Recovery events
+    public static final String EVENT_VM_BACKUP_IMPORT_OFFERING = "BACKUP.IMPORT.OFFERING";
+    public static final String EVENT_VM_BACKUP_OFFERING_ASSIGN = "BACKUP.OFFERING.ASSIGN";
+    public static final String EVENT_VM_BACKUP_OFFERING_REMOVE = "BACKUP.OFFERING.REMOVE";
+    public static final String EVENT_VM_BACKUP_CREATE = "BACKUP.CREATE";
+    public static final String EVENT_VM_BACKUP_RESTORE = "BACKUP.RESTORE";
+    public static final String EVENT_VM_BACKUP_DELETE = "BACKUP.DELETE";
+    public static final String EVENT_VM_BACKUP_RESTORE_VOLUME_TO_VM = "BACKUP.RESTORE.VOLUME.TO.VM";
+    public static final String EVENT_VM_BACKUP_SCHEDULE_CONFIGURE = "BACKUP.SCHEDULE.CONFIGURE";
+    public static final String EVENT_VM_BACKUP_SCHEDULE_DELETE = "BACKUP.SCHEDULE.DELETE";
+
     // external network device events
     public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD";
     public static final String EVENT_EXTERNAL_NVP_CONTROLLER_DELETE = "PHYSICAL.NVPCONTROLLER.DELETE";
diff --git a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java
index da2c7d0..8a10964 100644
--- a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java
+++ b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java
@@ -19,13 +19,14 @@ package com.cloud.hypervisor;
 import java.util.List;
 import java.util.Map;
 
-import com.cloud.storage.StoragePool;
+import org.apache.cloudstack.backup.Backup;
 import org.apache.cloudstack.framework.config.ConfigKey;
 
 import com.cloud.agent.api.Command;
 import com.cloud.agent.api.to.NicTO;
 import com.cloud.agent.api.to.VirtualMachineTO;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.storage.StoragePool;
 import com.cloud.utils.Pair;
 import com.cloud.utils.component.Adapter;
 import com.cloud.vm.NicProfile;
@@ -86,6 +87,11 @@ public interface HypervisorGuru extends Adapter {
 
     Map<String, String> getClusterSettings(long vmId);
 
+    VirtualMachine importVirtualMachineFromBackup(long zoneId, long domainId, long accountId, long userId,
+                                                  String vmInternalName, Backup backup) throws Exception;
+
+    boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backup.VolumeInfo volumeInfo,
+                                                 VirtualMachine vm, long poolId, Backup backup) throws Exception;
     /**
      * Will generate commands to migrate a vm to a pool. For now this will only work for stopped VMs on Vmware.
      *
diff --git a/api/src/main/java/com/cloud/server/ResourceTag.java b/api/src/main/java/com/cloud/server/ResourceTag.java
index 99eb860..fb07762 100644
--- a/api/src/main/java/com/cloud/server/ResourceTag.java
+++ b/api/src/main/java/com/cloud/server/ResourceTag.java
@@ -29,6 +29,7 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit
         ISO(true, false),
         Volume(true, true),
         Snapshot(true, false),
+        Backup(true, false),
         Network(true, true),
         Nic(false, true),
         LoadBalancer(true, true),
diff --git a/api/src/main/java/com/cloud/vm/VirtualMachine.java b/api/src/main/java/com/cloud/vm/VirtualMachine.java
index d234c48..4d6014f 100644
--- a/api/src/main/java/com/cloud/vm/VirtualMachine.java
+++ b/api/src/main/java/com/cloud/vm/VirtualMachine.java
@@ -16,18 +16,21 @@
 // under the License.
 package com.cloud.vm;
 
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.api.Displayable;
+import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.kernel.Partition;
+
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.utils.fsm.StateMachine2;
 import com.cloud.utils.fsm.StateMachine2.Transition;
 import com.cloud.utils.fsm.StateMachine2.Transition.Impact;
 import com.cloud.utils.fsm.StateObject;
-import org.apache.cloudstack.acl.ControlledEntity;
-import org.apache.cloudstack.api.Displayable;
-import org.apache.cloudstack.kernel.Partition;
-
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Map;
 
 /**
  * VirtualMachine describes the properties held by a virtual machine
@@ -319,6 +322,12 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Partition,
 
     Long getDiskOfferingId();
 
+    Long getBackupOfferingId();
+
+    String getBackupExternalId();
+
+    List<Backup.VolumeInfo> getBackupVolumeList();
+
     Type getType();
 
     HypervisorType getHypervisorType();
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiCommandJobType.java b/api/src/main/java/org/apache/cloudstack/api/ApiCommandJobType.java
index d35598b..1cac1da 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiCommandJobType.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiCommandJobType.java
@@ -23,6 +23,7 @@ public enum ApiCommandJobType {
     Volume,
     ConsoleProxy,
     Snapshot,
+    Backup,
     Template,
     Iso,
     SystemVm,
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 015061c..1c6fd8b 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -33,6 +33,9 @@ public class ApiConstants {
     public static final String APPLIED = "applied";
     public static final String LIST_LB_VMIPS = "lbvmips";
     public static final String AVAILABLE = "available";
+    public static final String BACKUP_ID = "backupid";
+    public static final String BACKUP_OFFERING_NAME = "backupofferingname";
+    public static final String BACKUP_OFFERING_ID = "backupofferingid";
     public static final String BITS = "bits";
     public static final String BOOTABLE = "bootable";
     public static final String BIND_DN = "binddn";
@@ -144,6 +147,7 @@ public class ApiConstants {
     public static final String EXTRA_DHCP_OPTION_NAME = "extradhcpoptionname";
     public static final String EXTRA_DHCP_OPTION_CODE = "extradhcpoptioncode";
     public static final String EXTRA_DHCP_OPTION_VALUE = "extradhcpvalue";
+    public static final String EXTERNAL = "external";
     public static final String FENCE = "fence";
     public static final String FETCH_LATEST = "fetchlatest";
     public static final String FIRSTNAME = "firstname";
@@ -366,6 +370,7 @@ public class ApiConstants {
     public static final String VALUE = "value";
     public static final String VIRTUAL_MACHINE_ID = "virtualmachineid";
     public static final String VIRTUAL_MACHINE_IDS = "virtualmachineids";
+    public static final String VIRTUAL_MACHINE_NAME = "virtualmachinename";
     public static final String VIRTUAL_MACHINE_ID_IP = "vmidipmap";
     public static final String VIRTUAL_MACHINE_COUNT = "virtualmachinecount";
     public static final String USAGE_ID = "usageid";
@@ -385,6 +390,7 @@ public class ApiConstants {
     public static final String VNET = "vnet";
     public static final String IS_VOLATILE = "isvolatile";
     public static final String VOLUME_ID = "volumeid";
+    public static final String VOLUMES = "volumes";
     public static final String ZONE = "zone";
     public static final String ZONE_ID = "zoneid";
     public static final String ZONE_NAME = "zonename";
@@ -531,6 +537,7 @@ public class ApiConstants {
     public static final String REQUIRED = "required";
     public static final String RESTART_REQUIRED = "restartrequired";
     public static final String ALLOW_USER_CREATE_PROJECTS = "allowusercreateprojects";
+    public static final String ALLOW_USER_DRIVEN_BACKUPS = "allowuserdrivenbackups";
     public static final String CONSERVE_MODE = "conservemode";
     public static final String TRAFFIC_TYPE_IMPLEMENTOR = "traffictypeimplementor";
     public static final String KEYWORD = "keyword";
@@ -780,7 +787,7 @@ public class ApiConstants {
     }
 
     public enum VMDetails {
-        all, group, nics, stats, secgrp, tmpl, servoff, diskoff, iso, volume, min, affgrp;
+        all, group, nics, stats, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp;
     }
 
     public enum DomainDetails {
diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseBackupListCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseBackupListCmd.java
new file mode 100644
index 0000000..0aa8366
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/BaseBackupListCmd.java
@@ -0,0 +1,49 @@
+// 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.api;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cloudstack.api.response.BackupOfferingResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.context.CallContext;
+
+public abstract class BaseBackupListCmd extends BaseListCmd {
+
+    protected void setupResponseBackupOfferingsList(final List<BackupOffering> offerings, final Integer count) {
+        final ListResponse<BackupOfferingResponse> response = new ListResponse<>();
+        final List<BackupOfferingResponse> responses = new ArrayList<>();
+        for (final BackupOffering offering : offerings) {
+            if (offering == null) {
+                continue;
+            }
+            BackupOfferingResponse backupOfferingResponse = _responseGenerator.createBackupOfferingResponse(offering);
+            responses.add(backupOfferingResponse);
+        }
+        response.setResponses(responses, count);
+        response.setResponseName(getCommandName());
+        setResponseObject(response);
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java
index 510562b..57e03b3 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java
@@ -22,8 +22,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse;
-import org.apache.cloudstack.management.ManagementServerHost;
 import org.apache.cloudstack.affinity.AffinityGroup;
 import org.apache.cloudstack.affinity.AffinityGroupResponse;
 import org.apache.cloudstack.api.ApiConstants.HostDetails;
@@ -36,6 +34,8 @@ import org.apache.cloudstack.api.response.AsyncJobResponse;
 import org.apache.cloudstack.api.response.AutoScalePolicyResponse;
 import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse;
 import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse;
+import org.apache.cloudstack.api.response.BackupOfferingResponse;
+import org.apache.cloudstack.api.response.BackupScheduleResponse;
 import org.apache.cloudstack.api.response.CapacityResponse;
 import org.apache.cloudstack.api.response.ClusterResponse;
 import org.apache.cloudstack.api.response.ConditionResponse;
@@ -88,6 +88,7 @@ import org.apache.cloudstack.api.response.RemoteAccessVpnResponse;
 import org.apache.cloudstack.api.response.ResourceCountResponse;
 import org.apache.cloudstack.api.response.ResourceLimitResponse;
 import org.apache.cloudstack.api.response.ResourceTagResponse;
+import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse;
 import org.apache.cloudstack.api.response.SSHKeyPairResponse;
 import org.apache.cloudstack.api.response.SecurityGroupResponse;
 import org.apache.cloudstack.api.response.ServiceOfferingResponse;
@@ -111,6 +112,7 @@ import org.apache.cloudstack.api.response.UpgradeRouterTemplateResponse;
 import org.apache.cloudstack.api.response.UsageRecordResponse;
 import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.api.response.BackupResponse;
 import org.apache.cloudstack.api.response.VMSnapshotResponse;
 import org.apache.cloudstack.api.response.VirtualRouterProviderResponse;
 import org.apache.cloudstack.api.response.VlanIpRangeResponse;
@@ -119,7 +121,11 @@ import org.apache.cloudstack.api.response.VpcOfferingResponse;
 import org.apache.cloudstack.api.response.VpcResponse;
 import org.apache.cloudstack.api.response.VpnUsersResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
+import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.backup.BackupSchedule;
 import org.apache.cloudstack.config.Configuration;
+import org.apache.cloudstack.management.ManagementServerHost;
 import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule;
 import org.apache.cloudstack.region.PortableIp;
 import org.apache.cloudstack.region.PortableIpRange;
@@ -467,6 +473,12 @@ public interface ResponseGenerator {
 
     SSHKeyPairResponse createSSHKeyPairResponse(SSHKeyPair sshkeyPair, boolean privatekey);
 
+    BackupResponse createBackupResponse(Backup backup);
+
+    BackupScheduleResponse createBackupScheduleResponse(BackupSchedule backup);
+
+    BackupOfferingResponse createBackupOfferingResponse(BackupOffering policy);
+
     ManagementServerResponse createManagementResponse(ManagementServerHost mgmt);
 
     List<RouterHealthCheckResultResponse> createHealthCheckResponse(VirtualMachine router, List<RouterHealthCheckResult> healthCheckResults);
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/DeleteBackupOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/DeleteBackupOfferingCmd.java
new file mode 100644
index 0000000..a405fd6
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/DeleteBackupOfferingCmd.java
@@ -0,0 +1,92 @@
+// 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.api.command.admin.backup;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.BackupOfferingResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+
+@APICommand(name = DeleteBackupOfferingCmd.APINAME,
+        description = "Deletes a backup offering",
+        responseObject = SuccessResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin})
+public class DeleteBackupOfferingCmd extends BaseCmd {
+    public static final String APINAME = "deleteBackupOffering";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    ////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+            type = CommandType.UUID,
+            entityType = BackupOfferingResponse.class,
+            required = true,
+            description = "ID of the backup offering")
+    private Long id;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+        if (backupManager.deleteBackupOffering(getId())) {
+            SuccessResponse response = new SuccessResponse(getCommandName());
+            setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to remove backup offering: " + getId());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupOfferingCmd.java
new file mode 100644
index 0000000..f682f4c
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ImportBackupOfferingCmd.java
@@ -0,0 +1,145 @@
+// 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.api.command.admin.backup;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.BackupOfferingResponse;
+import org.apache.cloudstack.api.response.ZoneResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@APICommand(name = ImportBackupOfferingCmd.APINAME,
+        description = "Imports a backup offering using a backup provider",
+        responseObject = BackupOfferingResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin})
+public class ImportBackupOfferingCmd extends BaseAsyncCmd {
+    public static final String APINAME = "importBackupOffering";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    ////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true,
+            description = "the name of the backup offering")
+    private String name;
+
+    @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, required = true,
+            description = "the description of the backup offering")
+    private String description;
+
+    @Parameter(name = ApiConstants.EXTERNAL_ID,
+            type = CommandType.STRING,
+            required = true,
+            description = "The backup offering ID (from backup provider side)")
+    private String externalId;
+
+    @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class,
+            description = "The zone ID", required = true)
+    private Long zoneId;
+
+    @Parameter(name = ApiConstants.ALLOW_USER_DRIVEN_BACKUPS, type = CommandType.BOOLEAN,
+            description = "Whether users are allowed to create adhoc backups and backup schedules", required = true)
+    private Boolean userDrivenBackups;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getName() {
+        return name;
+    }
+
+    public String getExternalId() {
+        return externalId;
+    }
+
+    public Long getZoneId() {
+        return zoneId;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public Boolean getUserDrivenBackups() {
+        return userDrivenBackups == null ? false : userDrivenBackups;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+        try {
+            BackupOffering policy = backupManager.importBackupOffering(this);
+            if (policy != null) {
+                BackupOfferingResponse response = _responseGenerator.createBackupOfferingResponse(policy);
+                response.setResponseName(getCommandName());
+                setResponseObject(response);
+            } else {
+                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add a backup offering");
+            }
+        } catch (InvalidParameterValueException e) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage());
+        } catch (CloudRuntimeException e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_VM_BACKUP_IMPORT_OFFERING;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "Importing backup offering: " + name + " (external ID: " + externalId + ") on zone ID " + zoneId ;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListBackupProviderOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListBackupProviderOfferingsCmd.java
new file mode 100644
index 0000000..2e5657d
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListBackupProviderOfferingsCmd.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.api.command.admin.backup;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseBackupListCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.BackupOfferingResponse;
+import org.apache.cloudstack.api.response.ZoneResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.backup.BackupOffering;
+
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@APICommand(name = ListBackupProviderOfferingsCmd.APINAME,
+        description = "Lists external backup offerings of the provider",
+        responseObject = BackupOfferingResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin})
+public class ListBackupProviderOfferingsCmd extends BaseBackupListCmd {
+    public static final String APINAME = "listBackupProviderOfferings";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class,
+            required = true, description = "The zone ID")
+    private Long zoneId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getZoneId() {
+        return zoneId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    private void validateParameters() {
+        if (getZoneId() == null) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Please provide a valid zone ID ");
+        }
+    }
+
+    @Override
+    public void execute() throws ResourceUnavailableException, ServerApiException, ConcurrentOperationException {
+        validateParameters();
+        try {
+            final List<BackupOffering> backupOfferings = backupManager.listBackupProviderOfferings(getZoneId());
+            setupResponseBackupOfferingsList(backupOfferings, backupOfferings.size());
+        } catch (InvalidParameterValueException e) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage());
+        } catch (CloudRuntimeException e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + RESPONSE_SUFFIX;
+    }
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListBackupProvidersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListBackupProvidersCmd.java
new file mode 100644
index 0000000..2b4b735
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/backup/ListBackupProvidersCmd.java
@@ -0,0 +1,98 @@
+// 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.api.command.admin.backup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.BackupProviderResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.backup.BackupProvider;
+
+import com.cloud.user.Account;
+
+@APICommand(name = ListBackupProvidersCmd.APINAME,
+        description = "Lists Backup and Recovery providers",
+        responseObject = BackupProviderResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin})
+public class ListBackupProvidersCmd extends BaseCmd {
+    public static final String APINAME = "listBackupProviders";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List Backup and Recovery provider by name")
+    private String name;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    private void setupResponse(final List<BackupProvider> providers) {
+        final ListResponse<BackupProviderResponse> response = new ListResponse<>();
+        final List<BackupProviderResponse> responses = new ArrayList<>();
+        for (final BackupProvider provider : providers) {
+            if (provider == null || (getName() != null && !provider.getName().equals(getName()))) {
+                continue;
+            }
+            final BackupProviderResponse backupProviderResponse = new BackupProviderResponse();
+            backupProviderResponse.setName(provider.getName());
+            backupProviderResponse.setDescription(provider.getDescription());
+            backupProviderResponse.setObjectName("providers");
+            responses.add(backupProviderResponse);
+        }
+        response.setResponses(responses);
+        response.setResponseName(getCommandName());
+        setResponseObject(response);
+    }
+
+    @Override
+    public void execute() {
+        List<BackupProvider> providers = backupManager.listBackupProviders();
+        setupResponse(providers);
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AssignVirtualMachineToBackupOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AssignVirtualMachineToBackupOfferingCmd.java
new file mode 100644
index 0000000..b5c0986
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/AssignVirtualMachineToBackupOfferingCmd.java
@@ -0,0 +1,122 @@
+// 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.api.command.user.backup;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.BackupOfferingResponse;
+import org.apache.cloudstack.api.response.BackupResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+
+@APICommand(name = AssignVirtualMachineToBackupOfferingCmd.APINAME,
+        description = "Assigns a VM to a backup offering",
+        responseObject = BackupResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class AssignVirtualMachineToBackupOfferingCmd extends BaseAsyncCmd {
+    public static final String APINAME = "assignVirtualMachineToBackupOffering";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
+            type = CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "ID of the virtual machine")
+    private Long vmId;
+
+    @Parameter(name = ApiConstants.BACKUP_OFFERING_ID,
+            type = CommandType.UUID,
+            entityType = BackupOfferingResponse.class,
+            required = true,
+            description = "ID of the backup offering")
+    private Long offeringId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    public Long getOfferingId() {
+        return offeringId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+        try {
+            boolean result = backupManager.assignVMToBackupOffering(getVmId(), getOfferingId());
+            if (result) {
+                SuccessResponse response = new SuccessResponse(getCommandName());
+                this.setResponseObject(response);
+            } else {
+                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add VM to backup offering");
+            }
+        } catch (Exception e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_VM_BACKUP_OFFERING_ASSIGN;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "Assigning VM to backup offering ID: " + offeringId;
+    }
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupCmd.java
new file mode 100644
index 0000000..db7a276
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupCmd.java
@@ -0,0 +1,126 @@
+// 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.api.command.user.backup;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiCommandJobType;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCreateCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@APICommand(name = CreateBackupCmd.APINAME,
+        description = "Create VM backup",
+        responseObject = SuccessResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class CreateBackupCmd extends BaseAsyncCreateCmd {
+    public static final String APINAME = "createBackup";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
+            type = CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "ID of the VM")
+    private Long vmId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+        try {
+            boolean result = backupManager.createBackup(getVmId());
+            if (result) {
+                SuccessResponse response = new SuccessResponse(getCommandName());
+                response.setResponseName(getCommandName());
+                setResponseObject(response);
+            } else {
+                throw new CloudRuntimeException("Error while creating backup of VM");
+            }
+        } catch (Exception e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public ApiCommandJobType getInstanceType() {
+        return ApiCommandJobType.Backup;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_VM_BACKUP_CREATE;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "Creating backup for VM " + vmId;
+    }
+
+    @Override
+    public void create() throws ResourceAllocationException {
+    }
+
+    @Override
+    public Long getEntityId() {
+        return vmId;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java
new file mode 100644
index 0000000..e10386d
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java
@@ -0,0 +1,128 @@
+// 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.api.command.user.backup;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.BackupResponse;
+import org.apache.cloudstack.api.response.BackupScheduleResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.backup.BackupSchedule;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.utils.DateUtil;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@APICommand(name = CreateBackupScheduleCmd.APINAME,
+        description = "Creates a user-defined VM backup schedule",
+        responseObject = BackupResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class CreateBackupScheduleCmd extends BaseCmd {
+    public static final String APINAME = "createBackupSchedule";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
+            type = CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "ID of the VM for which schedule is to be defined")
+    private Long vmId;
+
+    @Parameter(name = ApiConstants.INTERVAL_TYPE,
+            type = CommandType.STRING,
+            required = true,
+            description = "valid values are HOURLY, DAILY, WEEKLY, and MONTHLY")
+    private String intervalType;
+
+    @Parameter(name = ApiConstants.SCHEDULE,
+            type = CommandType.STRING,
+            required = true,
+            description = "custom backup schedule, the format is:"
+            + "for HOURLY MM*, for DAILY MM:HH*, for WEEKLY MM:HH:DD (1-7)*, for MONTHLY MM:HH:DD (1-28)")
+    private String schedule;
+
+    @Parameter(name = ApiConstants.TIMEZONE,
+            type = CommandType.STRING,
+            required = true,
+            description = "Specifies a timezone for this command. For more information on the timezone parameter, see TimeZone Format.")
+    private String timezone;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    public DateUtil.IntervalType getIntervalType() {
+        return DateUtil.IntervalType.getIntervalType(intervalType);
+    }
+
+    public String getSchedule() {
+        return schedule;
+    }
+
+    public String getTimezone() {
+        return timezone;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ServerApiException {
+        try {
+            BackupSchedule schedule = backupManager.configureBackupSchedule(this);
+            if (schedule != null) {
+                BackupScheduleResponse response = _responseGenerator.createBackupScheduleResponse(schedule);
+                response.setResponseName(getCommandName());
+                setResponseObject(response);
+            } else {
+                throw new CloudRuntimeException("Error while creating backup schedule of VM");
+            }
+        } catch (Exception e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupCmd.java
new file mode 100644
index 0000000..32344d8
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupCmd.java
@@ -0,0 +1,111 @@
+// 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.api.command.user.backup;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.BackupResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@APICommand(name = DeleteBackupCmd.APINAME,
+        description = "Delete VM backup",
+        responseObject = SuccessResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class DeleteBackupCmd  extends BaseAsyncCmd {
+    public static final String APINAME = "deleteBackup";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+            type = CommandType.UUID,
+            entityType = BackupResponse.class,
+            required = true,
+            description = "id of the VM backup")
+    private Long backupId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return backupId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+        try {
+            boolean result = backupManager.deleteBackup(backupId);
+            if (result) {
+                SuccessResponse response = new SuccessResponse(getCommandName());
+                response.setResponseName(getCommandName());
+                setResponseObject(response);
+            } else {
+                throw new CloudRuntimeException("Error while deleting backup of VM");
+            }
+        } catch (Exception e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_VM_BACKUP_DELETE;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "Deleting backup ID " + backupId;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java
new file mode 100644
index 0000000..1c7b65c
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java
@@ -0,0 +1,99 @@
+// 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.api.command.user.backup;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@APICommand(name = DeleteBackupScheduleCmd.APINAME,
+        description = "Deletes the backup schedule of a VM",
+        responseObject = SuccessResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class DeleteBackupScheduleCmd  extends BaseCmd {
+    public static final String APINAME = "deleteBackupSchedule";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
+            type = CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "ID of the VM")
+    private Long vmId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+        try {
+            boolean result = backupManager.deleteBackupSchedule(getVmId());
+            if (result) {
+                SuccessResponse response = new SuccessResponse(getCommandName());
+                response.setResponseName(getCommandName());
+                setResponseObject(response);
+            } else {
+                throw new CloudRuntimeException("Failed to delete VM backup schedule");
+            }
+        } catch (Exception e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupOfferingsCmd.java
new file mode 100644
index 0000000..e745a6b
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupOfferingsCmd.java
@@ -0,0 +1,96 @@
+// 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.api.command.user.backup;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseBackupListCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.BackupOfferingResponse;
+import org.apache.cloudstack.api.response.ZoneResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.backup.BackupOffering;
+
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.utils.Pair;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@APICommand(name = ListBackupOfferingsCmd.APINAME,
+        description = "Lists backup offerings",
+        responseObject = BackupOfferingResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class ListBackupOfferingsCmd extends BaseBackupListCmd {
+    public static final String APINAME = "listBackupOfferings";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, entityType = BackupOfferingResponse.class,
+            description = "The backup offering ID")
+    private Long offeringId;
+
+    @Parameter(name = ApiConstants.ZONE_ID, type = BaseCmd.CommandType.UUID, entityType = ZoneResponse.class,
+            description = "The zone ID")
+    private Long zoneId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getZoneId() {
+        return zoneId;
+    }
+
+    public Long getOfferingId() {
+        return offeringId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ResourceUnavailableException, ServerApiException, ConcurrentOperationException {
+        try {
+            Pair<List<BackupOffering>, Integer> result = backupManager.listBackupOfferings(this);
+            setupResponseBackupOfferingsList(result.first(), result.second());
+        } catch (InvalidParameterValueException e) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage());
+        } catch (CloudRuntimeException e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + RESPONSE_SUFFIX;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmd.java
new file mode 100644
index 0000000..4068dc2
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmd.java
@@ -0,0 +1,100 @@
+// 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.api.command.user.backup;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.BackupScheduleResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.backup.BackupSchedule;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@APICommand(name = ListBackupScheduleCmd.APINAME,
+        description = "List backup schedule of a VM",
+        responseObject = BackupScheduleResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class ListBackupScheduleCmd extends BaseCmd {
+    public static final String APINAME = "listBackupSchedule";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
+            type = CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "ID of the VM")
+    private Long vmId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+        try{
+            BackupSchedule schedule = backupManager.listBackupSchedule(getVmId());
+            if (schedule != null) {
+                BackupScheduleResponse response = _responseGenerator.createBackupScheduleResponse(schedule);
+                response.setResponseName(getCommandName());
+                setResponseObject(response);
+            } else {
+                throw new CloudRuntimeException("No backup schedule exists for the VM");
+            }
+        } catch (Exception e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java
new file mode 100644
index 0000000..1e1e731
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java
@@ -0,0 +1,135 @@
+// 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.api.command.user.backup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.BackupResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.api.response.ZoneResponse;
+import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.utils.Pair;
+
+@APICommand(name = ListBackupsCmd.APINAME,
+        description = "Lists VM backups",
+        responseObject = BackupResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class ListBackupsCmd extends BaseListProjectAndAccountResourcesCmd {
+    public static final String APINAME = "listBackups";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+            type = CommandType.UUID,
+            entityType = BackupResponse.class,
+            description = "id of the backup")
+    private Long id;
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
+            type = CommandType.UUID,
+            entityType = UserVmResponse.class,
+            description = "id of the VM")
+    private Long vmId;
+
+    @Parameter(name = ApiConstants.ZONE_ID,
+            type = CommandType.UUID,
+            entityType = ZoneResponse.class,
+            description = "list backups by zone id")
+    private Long zoneId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    public Long getZoneId() {
+        return zoneId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    protected void setupResponseBackupList(final List<Backup> backups, final Integer count) {
+        final List<BackupResponse> responses = new ArrayList<>();
+        for (Backup backup : backups) {
+            if (backup == null) {
+                continue;
+            }
+            BackupResponse backupResponse = _responseGenerator.createBackupResponse(backup);
+            responses.add(backupResponse);
+        }
+        final ListResponse<BackupResponse> response = new ListResponse<>();
+        response.setResponses(responses, count);
+        response.setResponseName(getCommandName());
+        setResponseObject(response);
+    }
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+        try{
+            Pair<List<Backup>, Integer> result = backupManager.listBackups(this);
+            setupResponseBackupList(result.first(), result.second());
+        } catch (Exception e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RemoveVirtualMachineFromBackupOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RemoveVirtualMachineFromBackupOfferingCmd.java
new file mode 100644
index 0000000..28f03f8
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RemoveVirtualMachineFromBackupOfferingCmd.java
@@ -0,0 +1,118 @@
+// 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.api.command.user.backup;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+
+@APICommand(name = RemoveVirtualMachineFromBackupOfferingCmd.APINAME,
+        description = "Removes a VM from any existing backup offering",
+        responseObject = SuccessResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class RemoveVirtualMachineFromBackupOfferingCmd extends BaseAsyncCmd {
+    public static final String APINAME = "removeVirtualMachineFromBackupOffering";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
+            type = CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "ID of the virtual machine")
+    private Long vmId;
+
+    @Parameter(name = ApiConstants.FORCED,
+            type = CommandType.BOOLEAN,
+            description = "Whether to force remove VM from the backup offering that may also delete VM backups.")
+    private Boolean forced;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    public boolean getForced() {
+        return forced == null ? false : forced;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+        try {
+            boolean result = backupManager.removeVMFromBackupOffering(getVmId(), getForced());
+            if (result) {
+                SuccessResponse response = new SuccessResponse(getCommandName());
+                this.setResponseObject(response);
+            } else {
+                throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove VM from backup offering");
+            }
+        } catch (Exception e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_VM_BACKUP_OFFERING_REMOVE;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "Removing VM ID" + vmId + " from backup offering";
+    }
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupCmd.java
new file mode 100644
index 0000000..62fa9a1
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreBackupCmd.java
@@ -0,0 +1,111 @@
+// 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.api.command.user.backup;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.BackupResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@APICommand(name = RestoreBackupCmd.APINAME,
+        description = "Restores an existing stopped or deleted VM using a VM backup",
+        responseObject = SuccessResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class RestoreBackupCmd extends BaseAsyncCmd {
+    public static final String APINAME = "restoreBackup";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+            type = CommandType.UUID,
+            entityType = BackupResponse.class,
+            required = true,
+            description = "ID of the backup")
+    private Long backupId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getBackupId() {
+        return backupId;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+        try {
+            boolean result = backupManager.restoreBackup(backupId);
+            if (result) {
+                SuccessResponse response = new SuccessResponse(getCommandName());
+                response.setResponseName(getCommandName());
+                setResponseObject(response);
+            } else {
+                throw new CloudRuntimeException("Error while restoring VM from backup");
+            }
+        } catch (Exception e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_VM_BACKUP_RESTORE;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "Restoring VM from backup: " + backupId;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVolumeFromBackupAndAttachToVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVolumeFromBackupAndAttachToVMCmd.java
new file mode 100644
index 0000000..b5966a7
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/RestoreVolumeFromBackupAndAttachToVMCmd.java
@@ -0,0 +1,133 @@
+// 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.api.command.user.backup;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.api.response.BackupResponse;
+import org.apache.cloudstack.backup.BackupManager;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@APICommand(name = RestoreVolumeFromBackupAndAttachToVMCmd.APINAME,
+        description = "Restore and attach a backed up volume to VM",
+        responseObject = SuccessResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class RestoreVolumeFromBackupAndAttachToVMCmd extends BaseAsyncCmd {
+    public static final String APINAME = "restoreVolumeFromBackupAndAttachToVM";
+
+    @Inject
+    private BackupManager backupManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.BACKUP_ID,
+            type = CommandType.UUID,
+            entityType = BackupResponse.class,
+            required = true,
+            description = "ID of the VM backup")
+    private Long backupId;
+
+    @Parameter(name = ApiConstants.VOLUME_ID,
+            type = CommandType.STRING,
+            required = true,
+            description = "ID of the volume backed up")
+    private String volumeUuid;
+
+    @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
+            type = CommandType.UUID,
+            entityType = UserVmResponse.class,
+            required = true,
+            description = "id of the VM where to attach the restored volume")
+    private Long vmId;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getVolumeUuid() {
+        return volumeUuid;
+    }
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    public Long getBackupId() {
+        return backupId;
+    }
+
+    @Override
+    public String getCommandName() {
+        return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return CallContext.current().getCallingAccount().getId();
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+        try {
+            boolean result = backupManager.restoreBackupVolumeAndAttachToVM(volumeUuid, backupId, vmId);
+            if (result) {
+                SuccessResponse response = new SuccessResponse(getCommandName());
+                response.setResponseName(getCommandName());
+                setResponseObject(response);
+            } else {
+                throw new CloudRuntimeException("Error restoring volume and attaching to VM");
+            }
+        } catch (Exception e) {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_VM_BACKUP_RESTORE_VOLUME_TO_VM;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "Restoring volume "+ volumeUuid + " from backup " + backupId + " and attaching it to VM " + vmId;
+    }
+}
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/UpdateBackupScheduleCmd.java
similarity index 56%
copy from engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
copy to api/src/main/java/org/apache/cloudstack/api/command/user/backup/UpdateBackupScheduleCmd.java
index 89e2c83..5a02cf9 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/UpdateBackupScheduleCmd.java
@@ -14,21 +14,17 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.storage.dao;
 
-import java.util.List;
+package org.apache.cloudstack.api.command.user.backup;
 
-import com.cloud.storage.DiskOfferingVO;
-import com.cloud.utils.db.GenericDao;
-
-public interface DiskOfferingDao extends GenericDao<DiskOfferingVO, Long> {
-
-    List<DiskOfferingVO> findPrivateDiskOffering();
-
-    List<DiskOfferingVO> findPublicDiskOfferings();
-
-    DiskOfferingVO findByUniqueName(String uniqueName);
-
-    DiskOfferingVO persistDeafultDiskOffering(DiskOfferingVO offering);
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.response.BackupResponse;
 
+@APICommand(name = UpdateBackupScheduleCmd.APINAME,
+        description = "Updates a user-defined VM backup schedule",
+        responseObject = BackupResponse.class, since = "4.14.0",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class UpdateBackupScheduleCmd extends CreateBackupScheduleCmd {
+    public static final String APINAME = "updateBackupSchedule";
 }
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java
new file mode 100644
index 0000000..480ebcf
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java
@@ -0,0 +1,95 @@
+// 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.api.response;
+
+import java.util.Date;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.EntityReference;
+import org.apache.cloudstack.backup.BackupOffering;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+
+@EntityReference(value = BackupOffering.class)
+public class BackupOfferingResponse extends BaseResponse {
+
+    @SerializedName(ApiConstants.ID)
+    @Param(description = "ID of the backup offering")
+    private String id;
+
+    @SerializedName(ApiConstants.NAME)
+    @Param(description = "name for the backup offering")
+    private String name;
+
+    @SerializedName(ApiConstants.DESCRIPTION)
+    @Param(description = "description for the backup offering")
+    private String description;
+
+    @SerializedName(ApiConstants.EXTERNAL_ID)
+    @Param(description = "external ID on the provider side")
+    private String externalId;
+
+    @SerializedName(ApiConstants.ALLOW_USER_DRIVEN_BACKUPS)
+    @Param(description = "whether offering allows user driven ad-hoc/scheduled backups")
+    private Boolean userDrivenBackups;
+
+    @SerializedName(ApiConstants.ZONE_ID)
+    @Param(description = "zone ID")
+    private String zoneId;
+
+    @SerializedName(ApiConstants.ZONE_NAME)
+    @Param(description = "zone name")
+    private String zoneName;
+
+    @SerializedName(ApiConstants.CREATED)
+    @Param(description = "the date this backup offering was created")
+    private Date created;
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void setExternalId(String externalId) {
+        this.externalId = externalId;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public void setUserDrivenBackups(Boolean userDrivenBackups) {
+        this.userDrivenBackups = userDrivenBackups;
+    }
+
+    public void setZoneId(String zoneId) {
+        this.zoneId = zoneId;
+    }
+
+    public void setZoneName(String zoneName) {
+        this.zoneName = zoneName;
+    }
+
+    public void setCreated(Date created) {
+        this.created = created;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupProviderResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupProviderResponse.java
new file mode 100644
index 0000000..5227d85
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupProviderResponse.java
@@ -0,0 +1,53 @@
+// 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.api.response;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.EntityReference;
+import org.apache.cloudstack.backup.BackupProvider;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+
+@EntityReference(BackupProvider.class)
+public class BackupProviderResponse extends BaseResponse {
+    @SerializedName(ApiConstants.NAME)
+    @Param(description = "the CA service provider name")
+    private String name;
+
+    @SerializedName(ApiConstants.DESCRIPTION)
+    @Param(description = "the description of the CA service provider")
+    private String description;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java
new file mode 100644
index 0000000..d0c8e58
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java
@@ -0,0 +1,246 @@
+// 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.api.response;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.EntityReference;
+import org.apache.cloudstack.backup.Backup;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+
+@EntityReference(value = Backup.class)
+public class BackupResponse extends BaseResponse {
+
+    @SerializedName(ApiConstants.ID)
+    @Param(description = "ID of the VM backup")
+    private String id;
+
+    @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID)
+    @Param(description = "ID of the VM")
+    private String vmId;
+
+    @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME)
+    @Param(description = "name of the VM")
+    private String vmName;
+
+    @SerializedName(ApiConstants.EXTERNAL_ID)
+    @Param(description = "external backup id")
+    private String externalId;
+
+    @SerializedName(ApiConstants.TYPE)
+    @Param(description = "backup type")
+    private String type;
+
+    @SerializedName(ApiConstants.CREATED)
+    @Param(description = "backup date")
+    private String date;
+
+    @SerializedName(ApiConstants.SIZE)
+    @Param(description = "backup size in bytes")
+    private Long size;
+
+    @SerializedName(ApiConstants.VIRTUAL_SIZE)
+    @Param(description = "backup protected (virtual) size in bytes")
+    private Long protectedSize;
+
+    @SerializedName(ApiConstants.STATUS)
+    @Param(description = "backup status")
+    private Backup.Status status;
+
+    @SerializedName(ApiConstants.VOLUMES)
+    @Param(description = "backed up volumes")
+    private String volumes;
+
+    @SerializedName(ApiConstants.BACKUP_OFFERING_ID)
+    @Param(description = "backup offering id")
+    private String backupOfferingId;
+
+    @SerializedName(ApiConstants.BACKUP_OFFERING_NAME)
+    @Param(description = "backup offering name")
+    private String backupOfferingName;
+
+    @SerializedName(ApiConstants.ACCOUNT_ID)
+    @Param(description = "account id")
+    private String accountId;
+
+    @SerializedName(ApiConstants.ACCOUNT)
+    @Param(description = "account name")
+    private String account;
+
+    @SerializedName(ApiConstants.DOMAIN_ID)
+    @Param(description = "domain id")
+    private String domainId;
+
+    @SerializedName(ApiConstants.DOMAIN)
+    @Param(description = "domain name")
+    private String domain;
+
+    @SerializedName(ApiConstants.ZONE_ID)
+    @Param(description = "zone id")
+    private String zoneId;
+
+    @SerializedName(ApiConstants.ZONE)
+    @Param(description = "zone name")
+    private String zone;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getVmId() {
+        return vmId;
+    }
+
+    public void setVmId(String vmId) {
+        this.vmId = vmId;
+    }
+
+    public String getVmName() {
+        return vmName;
+    }
+
+    public void setVmName(String vmName) {
+        this.vmName = vmName;
+    }
+
+    public String getExternalId() {
+        return externalId;
+    }
+
+    public void setExternalId(String externalId) {
+        this.externalId = externalId;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getDate() {
+        return date;
+    }
+
+    public void setDate(String date) {
+        this.date = date;
+    }
+
+    public Long getSize() {
+        return size;
+    }
+
+    public void setSize(Long size) {
+        this.size = size;
+    }
+
+    public Long getProtectedSize() {
+        return protectedSize;
+    }
+
+    public void setProtectedSize(Long protectedSize) {
+        this.protectedSize = protectedSize;
+    }
+
+    public Backup.Status getStatus() {
+        return status;
+    }
+
+    public void setStatus(Backup.Status status) {
+        this.status = status;
+    }
+
+    public String getVolumes() {
+        return volumes;
+    }
+
+    public void setVolumes(String volumes) {
+        this.volumes = volumes;
+    }
+
+    public String getBackupOfferingId() {
+        return backupOfferingId;
+    }
+
+    public void setBackupOfferingId(String backupOfferingId) {
+        this.backupOfferingId = backupOfferingId;
+    }
+
+    public String getBackupOffering() {
+        return backupOfferingName;
+    }
+
+    public void setBackupOffering(String backupOfferingName) {
+        this.backupOfferingName = backupOfferingName;
+    }
+
+    public String getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(String accountId) {
+        this.accountId = accountId;
+    }
+
+    public String getAccount() {
+        return account;
+    }
+
+    public void setAccount(String account) {
+        this.account = account;
+    }
+
+    public String getDomainId() {
+        return domainId;
+    }
+
+    public void setDomainId(String domainId) {
+        this.domainId = domainId;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+
+    public String getZoneId() {
+        return zoneId;
+    }
+
+    public void setZoneId(String zoneId) {
+        this.zoneId = zoneId;
+    }
+
+    public String getZone() {
+        return zone;
+    }
+
+    public void setZone(String zone) {
+        this.zone = zone;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupRestorePointResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupRestorePointResponse.java
new file mode 100644
index 0000000..afb3e9f
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupRestorePointResponse.java
@@ -0,0 +1,66 @@
+// 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.api.response;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.EntityReference;
+import org.apache.cloudstack.backup.Backup;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+
+@EntityReference(value = Backup.RestorePoint.class)
+public class BackupRestorePointResponse extends BaseResponse {
+
+    @SerializedName(ApiConstants.ID)
+    @Param(description = "external id of the restore point")
+    private String id;
+
+    @SerializedName(ApiConstants.CREATED)
+    @Param(description = "created time")
+    private String created;
+
+    @SerializedName(ApiConstants.TYPE)
+    @Param(description = "restore point type")
+    private String type;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getCreated() {
+        return created;
+    }
+
+    public void setCreated(String created) {
+        this.created = created;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java
new file mode 100644
index 0000000..ba44f1e
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java
@@ -0,0 +1,91 @@
+// 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.api.response;
+
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.api.EntityReference;
+import org.apache.cloudstack.backup.BackupSchedule;
+
+import com.cloud.serializer.Param;
+import com.cloud.utils.DateUtil;
+import com.google.gson.annotations.SerializedName;
+
+@EntityReference(value = BackupSchedule.class)
+public class BackupScheduleResponse extends BaseResponse {
+
+    @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME)
+    @Param(description = "name of the VM")
+    private String vmName;
+
+    @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID)
+    @Param(description = "ID of the VM")
+    private String vmId;
+
+    @SerializedName("schedule")
+    @Param(description = "time the backup is scheduled to be taken.")
+    private String schedule;
+
+    @SerializedName("intervaltype")
+    @Param(description = "the interval type of the backup schedule")
+    private DateUtil.IntervalType intervalType;
+
+    @SerializedName("timezone")
+    @Param(description = "the time zone of the backup schedule")
+    private String timezone;
+
+    public String getVmName() {
+        return vmName;
+    }
+
+    public void setVmName(String vmName) {
+        this.vmName = vmName;
+    }
+
+    public String getVmId() {
+        return vmId;
+    }
+
+    public void setVmId(String vmId) {
+        this.vmId = vmId;
+    }
+
+    public String getSchedule() {
+        return schedule;
+    }
+
+    public void setSchedule(String schedule) {
+        this.schedule = schedule;
+    }
+
+    public DateUtil.IntervalType getIntervalType() {
+        return intervalType;
+    }
+
+    public void setIntervalType(DateUtil.IntervalType intervalType) {
+        this.intervalType = intervalType;
+    }
+
+    public String getTimezone() {
+        return timezone;
+    }
+
+    public void setTimezone(String timezone) {
+        this.timezone = timezone;
+    }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java
index 8a2f1a1..ca3ca87 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java
@@ -156,6 +156,14 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
     @Param(description = "the name of the disk offering of the virtual machine", since = "4.4")
     private String diskOfferingName;
 
+    @SerializedName(ApiConstants.BACKUP_OFFERING_ID)
+    @Param(description = "the ID of the backup offering of the virtual machine", since = "4.14")
+    private String backupOfferingId;
+
+    @SerializedName(ApiConstants.BACKUP_OFFERING_NAME)
+    @Param(description = "the name of the backup offering of the virtual machine", since = "4.14")
+    private String backupOfferingName;
+
     @SerializedName("forvirtualnetwork")
     @Param(description = "the virtual network for the service offering")
     private Boolean forVirtualNetwork;
@@ -439,6 +447,14 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
         return diskOfferingName;
     }
 
+    public String getBackupOfferingId() {
+        return backupOfferingId;
+    }
+
+    public String getBackupOfferingName() {
+        return backupOfferingName;
+    }
+
     public Boolean getForVirtualNetwork() {
         return forVirtualNetwork;
     }
@@ -697,6 +713,14 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co
         this.diskOfferingName = diskOfferingName;
     }
 
+    public void setBackupOfferingId(String backupOfferingId) {
+        this.backupOfferingId = backupOfferingId;
+    }
+
+    public void setBackupOfferingName(String backupOfferingName) {
+        this.backupOfferingName = backupOfferingName;
+    }
+
     public void setCpuNumber(Integer cpuNumber) {
         this.cpuNumber = cpuNumber;
     }
diff --git a/api/src/main/java/org/apache/cloudstack/backup/Backup.java b/api/src/main/java/org/apache/cloudstack/backup/Backup.java
new file mode 100644
index 0000000..e6aa238
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/backup/Backup.java
@@ -0,0 +1,142 @@
+//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
+//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;
+
+import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.api.Identity;
+import org.apache.cloudstack.api.InternalIdentity;
+
+import com.cloud.storage.Volume;
+import com.cloud.utils.StringUtils;
+
+public interface Backup extends ControlledEntity, InternalIdentity, Identity {
+
+    enum Status {
+        Allocated, Queued, BackingUp, BackedUp, Error, Failed, Restoring, Removed, Expunged
+    }
+
+    class Metric {
+        private Long backupSize = 0L;
+        private Long dataSize = 0L;
+
+        public Metric(final Long backupSize, final Long dataSize) {
+            this.backupSize = backupSize;
+            this.dataSize = dataSize;
+        }
+
+        public Long getBackupSize() {
+            return backupSize;
+        }
+
+        public Long getDataSize() {
+            return dataSize;
+        }
+
+        public void setBackupSize(Long backupSize) {
+            this.backupSize = backupSize;
+        }
+
+        public void setDataSize(Long dataSize) {
+            this.dataSize = dataSize;
+        }
+    }
+
+    class RestorePoint {
+        private String id;
+        private String created;
+        private String type;
+
+        public RestorePoint(String id, String created, String type) {
+            this.id = id;
+            this.created = created;
+            this.type = type;
+        }
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        public String getCreated() {
+            return created;
+        }
+
+        public void setCreated(String created) {
+            this.created = created;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public void setType(String type) {
+            this.type = type;
+        }
+    }
+
+    class VolumeInfo {
+        private String uuid;
+        private Volume.Type type;
+        private Long size;
+        private String path;
+
+        public VolumeInfo(String uuid, String path, Volume.Type type, Long size) {
+            this.uuid = uuid;
+            this.type = type;
+            this.size = size;
+            this.path = path;
+        }
+
+        public String getUuid() {
+            return uuid;
+        }
+
+        public Volume.Type getType() {
+            return type;
+        }
+
+        public void setType(Volume.Type type) {
+            this.type = type;
+        }
+
+        public String getPath() {
+            return path;
+        }
+
+        public Long getSize() {
+            return size;
+        }
+
+        @Override
+        public String toString() {
+            return StringUtils.join(":", uuid, path, type, size);
+        }
+    }
+
+    long getVmId();
+    String getExternalId();
+    String getType();
+    String getDate();
+    Backup.Status getStatus();
+    Long getSize();
+    Long getProtectedSize();
+    long getZoneId();
+}
diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java
new file mode 100644
index 0000000..7c9d3b6
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java
@@ -0,0 +1,140 @@
+// 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;
+
+import java.util.List;
+
+import org.apache.cloudstack.api.command.admin.backup.ImportBackupOfferingCmd;
+import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd;
+import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd;
+import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
+
+import com.cloud.utils.Pair;
+import com.cloud.utils.component.Manager;
+import com.cloud.utils.component.PluggableService;
+
+/**
+ * Backup and Recover Manager Interface
+ */
+public interface BackupManager extends BackupService, Configurable, PluggableService, Manager {
+
+    ConfigKey<Boolean> BackupFrameworkEnabled = new ConfigKey<>("Advanced", Boolean.class,
+            "backup.framework.enabled",
+            "false",
+            "Is backup and recovery framework enabled.", true, ConfigKey.Scope.Zone);
+
+    ConfigKey<String> BackupProviderPlugin = new ConfigKey<>("Advanced", String.class,
+            "backup.framework.provider.plugin",
+            "dummy",
+            "The backup and recovery provider plugin.", true, ConfigKey.Scope.Zone);
+
+    ConfigKey<Long> BackupSyncPollingInterval = new ConfigKey<>("Advanced", Long.class,
+            "backup.framework.sync.interval",
+            "300",
+            "The backup and recovery background sync task polling interval in seconds.", true);
+
+    /**
+     * List backup provider offerings
+     * @param zoneId zone id
+     */
+    List<BackupOffering> listBackupProviderOfferings(final Long zoneId);
+
+    /**
+     * Add a new Backup and Recovery policy to CloudStack by mapping an existing external backup offering to a name and description
+     * @param cmd import backup offering cmd
+     */
+    BackupOffering importBackupOffering(final ImportBackupOfferingCmd cmd);
+
+    /**
+     * List backup offerings
+     * @param ListBackupOfferingsCmd API cmd
+     */
+    Pair<List<BackupOffering>, Integer> listBackupOfferings(final ListBackupOfferingsCmd cmd);
+
+    /**
+     * Deletes a backup offering
+     */
+    boolean deleteBackupOffering(final Long policyId);
+
+    /**
+     * Assigns a VM to a backup offering
+     * @param vmId
+     * @param offeringId
+     * @return
+     */
+    boolean assignVMToBackupOffering(final Long vmId, final Long offeringId);
+
+    /**
+     * Removes a VM from a backup offering
+     * @param vmId
+     * @param forced
+     * @return
+     */
+    boolean removeVMFromBackupOffering(final Long vmId, final boolean forced);
+
+    /**
+     * Creates or Updates a VM backup schedule
+     * @param cmd
+     * @return
+     */
+    BackupSchedule configureBackupSchedule(CreateBackupScheduleCmd cmd);
+
+    /**
+     * Lists VM backup schedule for a VM
+     * @param vmId
+     * @return
+     */
+    BackupSchedule listBackupSchedule(Long vmId);
+
+    /**
+     * Deletes VM backup schedule for a VM
+     * @param vmId
+     * @return
+     */
+    boolean deleteBackupSchedule(Long vmId);
+
+    /**
+     * Creates backup of a VM
+     * @param vmId Virtual Machine ID
+     * @return returns operation success
+     */
+    boolean createBackup(final Long vmId);
+
+    /**
+     * List existing backups for a VM
+     */
+    Pair<List<Backup>, Integer> listBackups(final ListBackupsCmd cmd);
+
+    /**
+     * Restore a full VM from backup
+     */
+    boolean restoreBackup(final Long backupId);
+
+    /**
+     * Restore a backed up volume and attach it to a VM
+     */
+    boolean restoreBackupVolumeAndAttachToVM(final String backedUpVolumeUuid, final Long backupId, final Long vmId) throws Exception;
+
+    /**
+     * Deletes a backup
+     * @return returns operation success
+     */
+    boolean deleteBackup(final Long backupId);
+}
diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupOffering.java b/api/src/main/java/org/apache/cloudstack/backup/BackupOffering.java
new file mode 100644
index 0000000..156c9cd
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/backup/BackupOffering.java
@@ -0,0 +1,32 @@
+//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
+//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;
+
+import java.util.Date;
+
+import org.apache.cloudstack.api.Identity;
+import org.apache.cloudstack.api.InternalIdentity;
+
+public interface BackupOffering extends InternalIdentity, Identity {
+    String getExternalId();
+    String getName();
+    String getDescription();
+    long getZoneId();
+    boolean isUserDrivenBackupAllowed();
+    String getProvider();
+    Date getCreated();
+}
diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java
new file mode 100644
index 0000000..ff05a38
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java
@@ -0,0 +1,111 @@
+//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
+//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;
+
+import java.util.List;
+import java.util.Map;
+
+import com.cloud.utils.Pair;
+import com.cloud.vm.VirtualMachine;
+
+public interface BackupProvider {
+
+    /**
+     * Returns the unique name of the provider
+     * @return returns provider name
+     */
+    String getName();
+
+    /**
+     * Returns description about the backup and recovery provider plugin
+     * @return returns description
+     */
+    String getDescription();
+
+    /**
+     * Returns the list of existing backup policies on the provider
+     * @return backup policies list
+     */
+    List<BackupOffering> listBackupOfferings(Long zoneId);
+
+    /**
+     * True if a backup offering exists on the backup provider
+     */
+    boolean isValidProviderOffering(Long zoneId, String uuid);
+
+    /**
+     * Assign a VM to a backup offering or policy
+     * @param vm
+     * @param backup
+     * @param policy
+     * @return
+     */
+    boolean assignVMToBackupOffering(VirtualMachine vm, BackupOffering backupOffering);
+
+    /**
+     * Removes a VM from a backup offering or policy
+     * @param vm
+     * @return
+     */
+    boolean removeVMFromBackupOffering(VirtualMachine vm);
+
+    /**
+     * Whether the provide will delete backups on removal of VM from the offfering
+     * @return boolean result
+     */
+    boolean willDeleteBackupsOnOfferingRemoval();
+
+    /**
+     * Starts and creates an adhoc backup process
+     * for a previously registered VM backup
+     * @param backup
+     * @return
+     */
+    boolean takeBackup(VirtualMachine vm);
+
+    /**
+     * Delete an existing backup
+     * @param backup
+     * @return
+     */
+    boolean deleteBackup(Backup backup);
+
+    /**
+     * Restore VM from backup
+     */
+    boolean restoreVMFromBackup(VirtualMachine vm, Backup backup);
+
+    /**
+     * Restore a volume from a backup
+     */
+    Pair<Boolean, String> restoreBackedUpVolume(Backup backup, String volumeUuid, String hostIp, String dataStoreUuid);
+
+    /**
+     * Returns backup metrics for a list of VMs in a zone
+     * @param zoneId
+     * @param vms
+     * @return
+     */
+    Map<VirtualMachine, Backup.Metric> getBackupMetrics(Long zoneId, List<VirtualMachine> vms);
+
+    /**
+     * This method should reconcile and create backup entries for any backups created out-of-band
+     * @param vm
+     * @param metric
+     */
+    void syncBackups(VirtualMachine vm, Backup.Metric metric);
+}
diff --git a/utils/src/main/java/com/cloud/utils/UuidUtils.java b/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java
similarity index 64%
copy from utils/src/main/java/com/cloud/utils/UuidUtils.java
copy to api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java
index 9c4a756..d81dd73 100644
--- a/utils/src/main/java/com/cloud/utils/UuidUtils.java
+++ b/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java
@@ -1,4 +1,3 @@
-//
 // 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
@@ -15,20 +14,20 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-//
 
-package com.cloud.utils;
+package org.apache.cloudstack.backup;
 
-import org.apache.xerces.impl.xpath.regex.RegularExpression;
+import java.util.Date;
 
-public class UuidUtils {
+import org.apache.cloudstack.api.InternalIdentity;
 
-    public final static String first(String uuid) {
-        return uuid.substring(0, uuid.indexOf('-'));
-    }
+import com.cloud.utils.DateUtil;
 
-    public static boolean validateUUID(String uuid) {
-        RegularExpression regex = new RegularExpression("[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
-        return regex.matches(uuid);
-    }
-}
\ No newline at end of file
+public interface BackupSchedule extends InternalIdentity {
+    Long getVmId();
+    DateUtil.IntervalType getScheduleType();
+    String getSchedule();
+    String getTimezone();
+    Date getScheduledTimestamp();
+    Long getAsyncJobId();
+}
diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupService.java b/api/src/main/java/org/apache/cloudstack/backup/BackupService.java
new file mode 100644
index 0000000..d4beb62
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/backup/BackupService.java
@@ -0,0 +1,37 @@
+//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
+//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;
+
+import java.util.List;
+
+/**
+ * Backup and Recovery Services
+ */
+public interface BackupService {
+    /**
+     * Lists backup and recovery provider plugins
+     * @return list of providers
+     */
+    List<BackupProvider> listBackupProviders();
+
+    /**
+     * Find backup provider by zone ID
+     * @param zoneId zone id
+     * @return backup provider
+     */
+    BackupProvider getBackupProvider(final Long zoneId);
+}
diff --git a/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java b/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java
index d0b7006..48cff30 100644
--- a/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java
+++ b/api/src/main/java/org/apache/cloudstack/usage/UsageTypes.java
@@ -44,6 +44,7 @@ public class UsageTypes {
     public static final int VM_SNAPSHOT = 25;
     public static final int VOLUME_SECONDARY = 26;
     public static final int VM_SNAPSHOT_ON_PRIMARY = 27;
+    public static final int BACKUP = 28;
 
     public static List<UsageTypeResponse> listUsageTypes() {
         List<UsageTypeResponse> responseList = new ArrayList<UsageTypeResponse>();
@@ -68,6 +69,7 @@ public class UsageTypes {
         responseList.add(new UsageTypeResponse(VM_SNAPSHOT, "VM Snapshot storage usage"));
         responseList.add(new UsageTypeResponse(VOLUME_SECONDARY, "Volume on secondary storage usage"));
         responseList.add(new UsageTypeResponse(VM_SNAPSHOT_ON_PRIMARY, "VM Snapshot on primary storage usage"));
+        responseList.add(new UsageTypeResponse(BACKUP, "Backup storage usage"));
         return responseList;
     }
 }
diff --git a/client/pom.xml b/client/pom.xml
index 507101b..29ecdec 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -478,6 +478,11 @@
             <artifactId>cloud-plugin-integrations-prometheus-exporter</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-plugin-backup-dummy</artifactId>
+            <version>${project.version}</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
@@ -768,6 +773,33 @@
                                 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                     <resource>META-INF/spring.schemas</resource>
                                 </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                  <resource>META-INF/services/com.sun.tools.xjc.Plugin</resource>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                  <resource>META-INF/cxf/cxf.extension</resource>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
+                                  <resource>META-INF/extensions.xml</resource>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
+                                  <resource>META-INF/cxf/extensions.xml</resource>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                  <resource>META-INF/cxf/bus-extensions.txt</resource>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
+                                  <resource>META-INF/cxf/bus-extensions.xml</resource>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
+                                  <resource>META-INF/wsdl.plugin.xml</resource>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
+                                  <resource>META-INF/tools.service.validator.xml</resource>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
+                                  <resource>META-INF/cxf/java2wsbeans.xml</resource>
+                                </transformer>
                             </transformers>
                             <filters>
                                 <filter>
@@ -902,25 +934,20 @@
                     <artifactId>cloud-plugin-network-cisco-vnmc</artifactId>
                     <version>${project.version}</version>
                 </dependency>
-            </dependencies>
-        </profile>
-        <profile>
-            <id>mysqlha</id>
-            <activation>
-                <property>
-                    <name>noredist</name>
-                </property>
-            </activation>
-            <dependencies>
                 <dependency>
                     <groupId>org.apache.cloudstack</groupId>
-                    <artifactId>cloud-plugin-database-mysqlha</artifactId>
+                    <artifactId>cloud-plugin-api-vmware-sioc</artifactId>
+                    <version>${project.version}</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.apache.cloudstack</groupId>
+                    <artifactId>cloud-plugin-backup-veeam</artifactId>
                     <version>${project.version}</version>
                 </dependency>
             </dependencies>
         </profile>
         <profile>
-            <id>vmwaresioc</id>
+            <id>mysqlha</id>
             <activation>
                 <property>
                     <name>noredist</name>
@@ -929,7 +956,7 @@
             <dependencies>
                 <dependency>
                     <groupId>org.apache.cloudstack</groupId>
-                    <artifactId>cloud-plugin-api-vmware-sioc</artifactId>
+                    <artifactId>cloud-plugin-database-mysqlha</artifactId>
                     <version>${project.version}</version>
                 </dependency>
             </dependencies>
diff --git a/core/src/main/resources/META-INF/cloudstack/backup/module.properties b/core/src/main/resources/META-INF/cloudstack/backup/module.properties
new file mode 100644
index 0000000..b85b65c
--- /dev/null
+++ b/core/src/main/resources/META-INF/cloudstack/backup/module.properties
@@ -0,0 +1,21 @@
+#
+# 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.
+#
+
+name=backup
+parent=backend
diff --git a/core/src/main/resources/META-INF/cloudstack/backup/spring-core-lifecycle-backup-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/backup/spring-core-lifecycle-backup-context-inheritable.xml
new file mode 100644
index 0000000..175d45e
--- /dev/null
+++ b/core/src/main/resources/META-INF/cloudstack/backup/spring-core-lifecycle-backup-context-inheritable.xml
@@ -0,0 +1,32 @@
+<!--
+
+    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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
+>
+
+    <bean class="org.apache.cloudstack.spring.lifecycle.registry.RegistryLifecycle">
+        <property name="registry" ref="backupProvidersRegistry" />
+        <property name="typeClass" value="org.apache.cloudstack.backup.BackupProvider" />
+    </bean>
+
+</beans>
diff --git a/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml b/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml
index 2569d8b..affd441 100644
--- a/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml
+++ b/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml
@@ -328,4 +328,8 @@
           class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
     </bean>
 
+    <bean id="backupProvidersRegistry"
+          class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
+    </bean>
+
 </beans>
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java
index 1dd7941..a84e4d5 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java
@@ -121,6 +121,8 @@ public interface NetworkDao extends GenericDao<NetworkVO, Long>, StateDao<State,
 
     List<NetworkVO> listNetworkVO(List<Long> idset);
 
+    NetworkVO findByVlan(String vlan);
+
     List<NetworkVO> listByAccountIdNetworkName(long accountId, String name);
 
     List<NetworkVO> listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri, Network.PVlanType pVlanType);
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java
index 711da1f..eeee3d1 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java
@@ -719,6 +719,14 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
     }
 
     @Override
+    public NetworkVO findByVlan(String vlan) {
+        SearchCriteria<NetworkVO> sc = AllFieldsSearch.create();
+        sc.setParameters("broadcastType", BroadcastDomainType.Vlan);
+        sc.setParameters("broadcastUri", BroadcastDomainType.Vlan.toUri(vlan));
+        return findOneBy(sc);
+    }
+
+    @Override
     public List<NetworkVO> listByAccountIdNetworkName(final long accountId, final String name) {
         final SearchCriteria<NetworkVO> sc = AllFieldsSearch.create();
         sc.setParameters("account", accountId);
diff --git a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java
index 8ac44b9..623179c 100644
--- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java
+++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java
@@ -51,4 +51,6 @@ public interface ServiceOfferingDao extends GenericDao<ServiceOfferingVO, Long>
     ServiceOfferingVO getComputeOffering(ServiceOfferingVO serviceOffering, Map<String, String> customParameters);
 
     ServiceOfferingVO findDefaultSystemOffering(String offeringName, Boolean useLocalStorage);
+
+    List<ServiceOfferingVO> listPublicByCpuAndMemory(Integer cpus, Integer memory);
 }
diff --git a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java
index 76e867b..1440051 100644
--- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java
@@ -51,6 +51,7 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase<ServiceOfferingVO, Lo
 
     protected final SearchBuilder<ServiceOfferingVO> UniqueNameSearch;
     protected final SearchBuilder<ServiceOfferingVO> ServiceOfferingsByKeywordSearch;
+    protected final SearchBuilder<ServiceOfferingVO> PublicCpuRamSearch;
 
     public ServiceOfferingDaoImpl() {
         super();
@@ -64,6 +65,12 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase<ServiceOfferingVO, Lo
         ServiceOfferingsByKeywordSearch.or("name", ServiceOfferingsByKeywordSearch.entity().getName(), SearchCriteria.Op.EQ);
         ServiceOfferingsByKeywordSearch.or("displayText", ServiceOfferingsByKeywordSearch.entity().getDisplayText(), SearchCriteria.Op.EQ);
         ServiceOfferingsByKeywordSearch.done();
+
+        PublicCpuRamSearch = createSearchBuilder();
+        PublicCpuRamSearch.and("cpu", PublicCpuRamSearch.entity().getCpu(), SearchCriteria.Op.EQ);
+        PublicCpuRamSearch.and("ram", PublicCpuRamSearch.entity().getRamSize(), SearchCriteria.Op.EQ);
+        PublicCpuRamSearch.and("system_use", PublicCpuRamSearch.entity().isSystemUse(), SearchCriteria.Op.EQ);
+        PublicCpuRamSearch.done();
     }
 
     @Override
@@ -246,4 +253,13 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase<ServiceOfferingVO, Lo
         }
         return serviceOffering;
     }
+
+    @Override
+    public List<ServiceOfferingVO> listPublicByCpuAndMemory(Integer cpus, Integer memory) {
+        SearchCriteria<ServiceOfferingVO> sc = PublicCpuRamSearch.create();
+        sc.setParameters("cpu", cpus);
+        sc.setParameters("ram", memory);
+        sc.setParameters("system_use", false);
+        return listBy(sc);
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
index 89e2c83..3305752 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
@@ -19,6 +19,7 @@ package com.cloud.storage.dao;
 import java.util.List;
 
 import com.cloud.storage.DiskOfferingVO;
+import com.cloud.storage.Storage;
 import com.cloud.utils.db.GenericDao;
 
 public interface DiskOfferingDao extends GenericDao<DiskOfferingVO, Long> {
@@ -31,4 +32,6 @@ public interface DiskOfferingDao extends GenericDao<DiskOfferingVO, Long> {
 
     DiskOfferingVO persistDeafultDiskOffering(DiskOfferingVO offering);
 
+    List<DiskOfferingVO> listAllBySizeAndProvisioningType(long size, Storage.ProvisioningType provisioningType);
+
 }
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java
index d93a052..b9fa10c 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java
@@ -16,6 +16,10 @@
 // under the License.
 package com.cloud.storage.dao;
 
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
@@ -27,12 +31,15 @@ import org.springframework.stereotype.Component;
 
 import com.cloud.offering.DiskOffering.Type;
 import com.cloud.storage.DiskOfferingVO;
+import com.cloud.storage.Storage;
 import com.cloud.utils.db.Attribute;
 import com.cloud.utils.db.Filter;
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.db.SearchCriteria.Op;
+import com.cloud.utils.db.TransactionLegacy;
+import com.cloud.utils.exception.CloudRuntimeException;
 
 @Component
 public class DiskOfferingDaoImpl extends GenericDaoBase<DiskOfferingVO, Long> implements DiskOfferingDao {
@@ -43,7 +50,11 @@ public class DiskOfferingDaoImpl extends GenericDaoBase<DiskOfferingVO, Long> im
     private final SearchBuilder<DiskOfferingVO> PrivateDiskOfferingSearch;
     private final SearchBuilder<DiskOfferingVO> PublicDiskOfferingSearch;
     protected final SearchBuilder<DiskOfferingVO> UniqueNameSearch;
+    private final String SizeDiskOfferingSearch = "SELECT * FROM disk_offering WHERE " +
+            "disk_size = ? AND provisioning_type = ? AND removed IS NULL";
+
     private final Attribute _typeAttr;
+    protected final static long GB_UNIT_BYTES = 1024 * 1024 * 1024;
 
     protected DiskOfferingDaoImpl() {
         PrivateDiskOfferingSearch = createSearchBuilder();
@@ -132,6 +143,36 @@ public class DiskOfferingDaoImpl extends GenericDaoBase<DiskOfferingVO, Long> im
         }
     }
 
+    protected long getClosestDiskSizeInGB(long sizeInBytes) {
+        if (sizeInBytes < 0) {
+            throw new CloudRuntimeException("Disk size should be greater than 0 bytes, received: " + sizeInBytes + " bytes");
+        }
+        return (long) Math.ceil(1.0 * sizeInBytes / GB_UNIT_BYTES);
+    }
+
+    @Override
+    public List<DiskOfferingVO> listAllBySizeAndProvisioningType(long size, Storage.ProvisioningType provisioningType) {
+        StringBuilder sql = new StringBuilder(SizeDiskOfferingSearch);
+        TransactionLegacy txn = TransactionLegacy.currentTxn();
+        List<DiskOfferingVO> offerings = new ArrayList<>();
+        try(PreparedStatement pstmt = txn.prepareStatement(sql.toString());){
+            if(pstmt != null) {
+                pstmt.setLong(1, size);
+                pstmt.setString(2, provisioningType.toString());
+                try(ResultSet rs = pstmt.executeQuery()) {
+                    while (rs.next()) {
+                        offerings.add(toEntityBean(rs, false));
+                    }
+                } catch (SQLException e) {
+                    throw new CloudRuntimeException("Exception while listing disk offerings by size: " + e.getMessage(), e);
+                }
+            }
+            return offerings;
+        } catch (SQLException e) {
+            throw new CloudRuntimeException("Exception while listing disk offerings by size: " + e.getMessage(), e);
+        }
+    }
+
     @Override
     public boolean remove(Long id) {
         DiskOfferingVO diskOffering = createForUpdate();
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java
index 6216ef7..05afad6 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java
@@ -48,4 +48,8 @@ public interface VMTemplatePoolDao extends GenericDao<VMTemplateStoragePoolVO, L
     boolean templateAvailable(long templateId, long poolId);
 
     public VMTemplateStoragePoolVO findByHostTemplate(Long hostId, Long templateId);
+
+    VMTemplateStoragePoolVO findByPoolPath(Long poolId, String path);
+
+    List<VMTemplateStoragePoolVO> listByTemplatePath(String templatePath);
 }
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java
index bb3985f..3287470 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java
@@ -59,6 +59,7 @@ public class VMTemplatePoolDaoImpl extends GenericDaoBase<VMTemplateStoragePoolV
     protected final SearchBuilder<VMTemplateStoragePoolVO> TemplateStatesSearch;
     protected final SearchBuilder<VMTemplateStoragePoolVO> TemplatePoolStateSearch;
     protected final SearchBuilder<VMTemplateStoragePoolVO> updateStateSearch;
+    protected final SearchBuilder<VMTemplateStoragePoolVO> templatePathSearch;
 
     protected static final String UPDATE_TEMPLATE_HOST_REF = "UPDATE template_spool_ref SET download_state = ?, download_pct= ?, last_updated = ? "
         + ", error_str = ?, local_path = ?, job_id = ? " + "WHERE pool_id = ? and template_id = ?";
@@ -114,6 +115,12 @@ public class VMTemplatePoolDaoImpl extends GenericDaoBase<VMTemplateStoragePoolV
         updateStateSearch.and("state", updateStateSearch.entity().getState(), Op.EQ);
         updateStateSearch.and("updatedCount", updateStateSearch.entity().getUpdatedCount(), Op.EQ);
         updateStateSearch.done();
+
+        templatePathSearch = createSearchBuilder();
+        templatePathSearch.and("pool_id", templatePathSearch.entity().getPoolId(), Op.EQ);
+        templatePathSearch.and("local_path", templatePathSearch.entity().getLocalDownloadPath(), Op.EQ);
+        templatePathSearch.and("install_path", templatePathSearch.entity().getInstallPath(), Op.EQ);
+        templatePathSearch.done();
     }
 
     @Override
@@ -261,6 +268,23 @@ public class VMTemplatePoolDaoImpl extends GenericDaoBase<VMTemplateStoragePoolV
     }
 
     @Override
+    public VMTemplateStoragePoolVO findByPoolPath(Long poolId, String path) {
+        SearchCriteria<VMTemplateStoragePoolVO> sc = templatePathSearch.create();
+        sc.setParameters("local_path", path);
+        sc.setParameters("install_path", path);
+        sc.setParameters("pool_id", poolId);
+        return findOneBy(sc);
+    }
+
+    @Override
+    public List<VMTemplateStoragePoolVO> listByTemplatePath(String templatePath) {
+        SearchCriteria<VMTemplateStoragePoolVO> sc = templatePathSearch.create();
+        sc.setParameters("local_path", templatePath);
+        sc.setParameters("install_path", templatePath);
+        return listBy(sc);
+    }
+
+    @Override
     public boolean updateState(State currentState, Event event, State nextState, DataObjectInStore vo, Object data) {
         VMTemplateStoragePoolVO templatePool = (VMTemplateStoragePoolVO)vo;
         Long oldUpdated = templatePool.getUpdatedCount();
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java
index 2741307..b410f48 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java
@@ -46,6 +46,8 @@ public interface VolumeDao extends GenericDao<VolumeVO, Long>, StateDao<Volume.S
 
     List<VolumeVO> findByInstanceAndType(long id, Volume.Type vType);
 
+    List<VolumeVO> findIncludingRemovedByInstanceAndType(long id, Volume.Type vType);
+
     List<VolumeVO> findByInstanceIdAndPoolId(long instanceId, long poolId);
 
     List<VolumeVO> findByInstanceIdDestroyed(long vmId);
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java
index 974d8e6..12e658a 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java
@@ -191,6 +191,16 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
     }
 
     @Override
+    public List<VolumeVO> findIncludingRemovedByInstanceAndType(long id, Type vType) {
+        SearchCriteria<VolumeVO> sc = AllFieldsSearch.create();
+        sc.setParameters("instanceId", id);
+        if (vType != null) {
+            sc.setParameters("vType", vType.toString());
+        }
+        return listIncludingRemovedBy(sc);
+    }
+
+    @Override
     public List<VolumeVO> findByInstanceIdDestroyed(long vmId) {
         SearchCriteria<VolumeVO> sc = AllFieldsSearch.create();
         sc.setParameters("instanceId", vmId);
diff --git a/engine/schema/src/main/java/com/cloud/usage/UsageBackupVO.java b/engine/schema/src/main/java/com/cloud/usage/UsageBackupVO.java
new file mode 100644
index 0000000..43e3974
--- /dev/null
+++ b/engine/schema/src/main/java/com/cloud/usage/UsageBackupVO.java
@@ -0,0 +1,172 @@
+// 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 com.cloud.usage;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.apache.cloudstack.api.InternalIdentity;
+
+@Entity
+@Table(name = "usage_backup")
+public class UsageBackupVO implements InternalIdentity {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private Long id;
+
+    @Column(name = "zone_id")
+    private long zoneId;
+
+    @Column(name = "account_id")
+    private long accountId;
+
+    @Column(name = "domain_id")
+    private long domainId;
+
+    @Column(name = "vm_id")
+    private long vmId;
+
+    @Column(name = "backup_offering_id")
+    private long backupOfferingId;
+
+    @Column(name = "size")
+    private long size;
+
+    @Column(name = "protected_size")
+    private long protectedSize;
+
+    @Column(name = "created")
+    @Temporal(value = TemporalType.TIMESTAMP)
+    private Date created = null;
+
+    @Column(name = "removed")
+    @Temporal(value = TemporalType.TIMESTAMP)
+    private Date removed;
+
+    protected UsageBackupVO() {
+    }
+
+    public UsageBackupVO(long zoneId, long accountId, long domainId, long vmId, long backupOfferingId, Date created) {
+        this.zoneId = zoneId;
+        this.accountId = accountId;
+        this.domainId = domainId;
+        this.vmId = vmId;
+        this.backupOfferingId = backupOfferingId;
+        this.created = created;
+    }
+
+    public UsageBackupVO(long id, long zoneId, long accountId, long domainId, long vmId, long backupOfferingId, long size, long protectedSize, Date created, Date removed) {
+        this.id = id;
+        this.zoneId = zoneId;
+        this.accountId = accountId;
+        this.domainId = domainId;
+        this.vmId = vmId;
+        this.backupOfferingId = backupOfferingId;
+        this.size = size;
+        this.protectedSize = protectedSize;
+        this.created = created;
+        this.removed = removed;
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    public long getZoneId() {
+        return zoneId;
+    }
+
+    public void setZoneId(long zoneId) {
+        this.zoneId = zoneId;
+    }
+
+    public long getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(long accountId) {
+        this.accountId = accountId;
+    }
+
+    public long getDomainId() {
+        return domainId;
+    }
+
+    public void setDomainId(long domainId) {
+        this.domainId = domainId;
+    }
+
+    public long getVmId() {
+        return vmId;
+    }
+
+    public void setVmId(long vmId) {
+        this.vmId = vmId;
+    }
+
+    public long getBackupOfferingId() {
+        return backupOfferingId;
+    }
+
+    public void setBackupOfferingId(long backupOfferingId) {
+        this.backupOfferingId = backupOfferingId;
+    }
+
+    public long getSize() {
+        return size;
+    }
+
+    public void setSize(long size) {
+        this.size = size;
+    }
+
+    public long getProtectedSize() {
+        return protectedSize;
+    }
+
+    public void setProtectedSize(long protectedSize) {
+        this.protectedSize = protectedSize;
+    }
+
+    public Date getCreated() {
+        return created;
+    }
+
+    public void setCreated(Date created) {
+        this.created = created;
+    }
+
+    public Date getRemoved() {
+        return removed;
+    }
+
+    public void setRemoved(Date removed) {
+        this.removed = removed;
+    }
+}
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java
similarity index 65%
copy from engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
copy to engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java
index 89e2c83..fb93c01 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
+++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java
@@ -14,21 +14,20 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.storage.dao;
 
-import java.util.List;
-
-import com.cloud.storage.DiskOfferingVO;
-import com.cloud.utils.db.GenericDao;
-
-public interface DiskOfferingDao extends GenericDao<DiskOfferingVO, Long> {
+package com.cloud.usage.dao;
 
-    List<DiskOfferingVO> findPrivateDiskOffering();
-
-    List<DiskOfferingVO> findPublicDiskOfferings();
+import java.util.Date;
+import java.util.List;
 
-    DiskOfferingVO findByUniqueName(String uniqueName);
+import org.apache.cloudstack.backup.Backup;
 
-    DiskOfferingVO persistDeafultDiskOffering(DiskOfferingVO offering);
+import com.cloud.usage.UsageBackupVO;
+import com.cloud.utils.db.GenericDao;
+import com.cloud.vm.VirtualMachine;
 
+public interface UsageBackupDao extends GenericDao<UsageBackupVO, Long> {
+    void updateMetrics(VirtualMachine vm, Backup.Metric metric);
+    void removeUsage(Long accountId, Long zoneId, Long backupId);
+    List<UsageBackupVO> getUsageRecords(Long accountId, Date startDate, Date endDate);
 }
diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java
new file mode 100644
index 0000000..35b86bc
--- /dev/null
+++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java
@@ -0,0 +1,139 @@
+// 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 com.cloud.usage.dao;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.apache.cloudstack.backup.Backup;
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import com.cloud.usage.UsageBackupVO;
+import com.cloud.utils.DateUtil;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.QueryBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionLegacy;
+import com.cloud.utils.db.TransactionStatus;
+import com.cloud.vm.VirtualMachine;
+
+@Component
+public class UsageBackupDaoImpl extends GenericDaoBase<UsageBackupVO, Long> implements UsageBackupDao {
+    public static final Logger LOGGER = Logger.getLogger(UsageBackupDaoImpl.class);
+    protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, vm_id, backup_offering_id, size, protected_size, created, removed FROM cloud_usage.usage_backup WHERE " +
+            " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " +
+            " OR ((created <= ?) AND (removed >= ?)))";
+
+    @Override
+    public void updateMetrics(final VirtualMachine vm, Backup.Metric metric) {
+        boolean result = Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback<Boolean>() {
+            @Override
+            public Boolean doInTransaction(final TransactionStatus status) {
+                final QueryBuilder<UsageBackupVO> qb = QueryBuilder.create(UsageBackupVO.class);
+                qb.and(qb.entity().getVmId(), SearchCriteria.Op.EQ, vm.getId());
+                final UsageBackupVO entry = findOneBy(qb.create());
+                if (entry == null) {
+                    return false;
+                }
+                entry.setSize(metric.getBackupSize());
+                entry.setProtectedSize(metric.getDataSize());
+                return update(entry.getId(), entry);
+            }
+        });
+        if (!result) {
+            LOGGER.trace("Failed to update backup metrics for VM ID: " + vm.getId());
+        }
+    }
+
+    @Override
+    public void removeUsage(Long accountId, Long zoneId, Long vmId) {
+        boolean result = Transaction.execute(TransactionLegacy.USAGE_DB, new TransactionCallback<Boolean>() {
+            @Override
+            public Boolean doInTransaction(final TransactionStatus status) {
+                final QueryBuilder<UsageBackupVO> qb = QueryBuilder.create(UsageBackupVO.class);
+                qb.and(qb.entity().getAccountId(), SearchCriteria.Op.EQ, accountId);
+                qb.and(qb.entity().getZoneId(), SearchCriteria.Op.EQ, zoneId);
+                qb.and(qb.entity().getVmId(), SearchCriteria.Op.EQ, vmId);
+                final UsageBackupVO entry = findOneBy(qb.create());
+                return remove(qb.create()) > 0;
+            }
+        });
+        if (!result) {
+            LOGGER.warn("Failed to remove usage entry for backup of VM ID: " + vmId);
+        }
+    }
+
+    @Override
+    public List<UsageBackupVO> getUsageRecords(Long accountId, Date startDate, Date endDate) {
+        List<UsageBackupVO> usageRecords = new ArrayList<UsageBackupVO>();
+        TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
+        PreparedStatement pstmt;
+        try {
+            int i = 1;
+            pstmt = txn.prepareAutoCloseStatement(GET_USAGE_RECORDS_BY_ACCOUNT);
+            pstmt.setLong(i++, accountId);
+
+            pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate));
+            pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate));
+            pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate));
+            pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate));
+            pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate));
+            pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), startDate));
+            pstmt.setString(i++, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), endDate));
+
+            ResultSet rs = pstmt.executeQuery();
+            while (rs.next()) {
+                //id, zone_id, account_id, domain_id, vm_id, disk_offering_id, size, created, processed
+                Long id = Long.valueOf(rs.getLong(1));
+                Long zoneId = Long.valueOf(rs.getLong(2));
+                Long acctId = Long.valueOf(rs.getLong(3));
+                Long domId = Long.valueOf(rs.getLong(4));
+                Long vmId = Long.valueOf(rs.getLong(5));
+                Long backupOfferingId = Long.valueOf(rs.getLong(6));
+                Long size = Long.valueOf(rs.getLong(7));
+                Long pSize = Long.valueOf(rs.getLong(8));
+                Date createdDate = null;
+                Date removedDate = null;
+                String createdTS = rs.getString(9);
+                String removedTS = rs.getString(10);
+
+                if (createdTS != null) {
+                    createdDate = DateUtil.parseDateString(s_gmtTimeZone, createdTS);
+                }
+                if (removedTS != null) {
+                    removedDate = DateUtil.parseDateString(s_gmtTimeZone, removedTS);
+                }
+                usageRecords.add(new UsageBackupVO(id, zoneId, acctId, domId, vmId, backupOfferingId, size, pSize, createdDate, removedDate));
+            }
+        } catch (Exception e) {
+            txn.rollback();
+            LOGGER.warn("Error getting VM backup usage records", e);
+        } finally {
+            txn.close();
+        }
+
+        return usageRecords;
+    }
+}
diff --git a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java
index b0ebf24..5c81e55 100644
--- a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java
+++ b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java
@@ -16,14 +16,14 @@
 // under the License.
 package com.cloud.vm;
 
-import com.cloud.hypervisor.Hypervisor.HypervisorType;
-import com.cloud.utils.db.Encrypt;
-import com.cloud.utils.db.GenericDao;
-import com.cloud.utils.db.StateMachine;
-import com.cloud.utils.fsm.FiniteStateObject;
-import com.cloud.vm.VirtualMachine.State;
-import org.apache.commons.codec.binary.Base64;
-import org.apache.log4j.Logger;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 
 import javax.persistence.Column;
 import javax.persistence.DiscriminatorColumn;
@@ -39,11 +39,19 @@ import javax.persistence.TableGenerator;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 import javax.persistence.Transient;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.Date;
-import java.util.Map;
-import java.util.UUID;
+
+import org.apache.cloudstack.backup.Backup;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.log4j.Logger;
+
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.utils.db.Encrypt;
+import com.cloud.utils.db.GenericDao;
+import com.cloud.utils.db.StateMachine;
+import com.cloud.utils.fsm.FiniteStateObject;
+import com.cloud.vm.VirtualMachine.State;
+import com.google.common.base.Strings;
+import com.google.gson.Gson;
 
 @Entity
 @Table(name = "vm_instance")
@@ -186,6 +194,15 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject<State, Vi
     @Column(name = "power_host", updatable = true)
     protected Long powerHostId;
 
+    @Column(name = "backup_offering_id")
+    protected Long backupOfferingId;
+
+    @Column(name = "backup_external_id")
+    protected String backupExternalId;
+
+    @Column(name = "backup_volumes")
+    protected String backupVolumes;
+
     public VMInstanceVO(long id, long serviceOfferingId, String name, String instanceName, Type type, Long vmTemplateId, HypervisorType hypervisorType, long guestOSId,
                         long domainId, long accountId, long userId, boolean haEnabled) {
         this.id = id;
@@ -483,6 +500,10 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject<State, Vi
         this.details = details;
     }
 
+    public void setRemoved(Date removed) {
+        this.removed = removed;
+    }
+
     transient String toString;
 
     @Override
@@ -573,4 +594,38 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject<State, Vi
     public PartitionType partitionType() {
         return PartitionType.VM;
     }
+
+    public long getUserId() {
+        return userId;
+    }
+
+    @Override
+    public Long getBackupOfferingId() {
+        return backupOfferingId;
+    }
+
+    public void setBackupOfferingId(Long backupOfferingId) {
+        this.backupOfferingId = backupOfferingId;
+    }
+
+    @Override
+    public String getBackupExternalId() {
+        return backupExternalId;
+    }
+
+    public void setBackupExternalId(String backupExternalId) {
+        this.backupExternalId = backupExternalId;
+    }
+
+    @Override
+    public List<Backup.VolumeInfo> getBackupVolumeList() {
+        if (Strings.isNullOrEmpty(this.backupVolumes)) {
+            return Collections.emptyList();
+        }
+        return Arrays.asList(new Gson().fromJson(this.backupVolumes, Backup.VolumeInfo[].class));
+    }
+
+    public void setBackupVolumes(String backupVolumes) {
+        this.backupVolumes = backupVolumes;
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java
index 316c2dd..f06fb0f 100644
--- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java
@@ -86,4 +86,6 @@ public interface NicDao extends GenericDao<NicVO, Long> {
     Long getPeerRouterId(String publicMacAddress, long routerId);
 
     List<NicVO> listByVmIdAndKeyword(long instanceId, String keyword);
+
+    NicVO findByInstanceIdAndMacAddress(long instanceId, String macAddress);
 }
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java
index 6314ca0..18630e8 100644
--- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java
@@ -364,4 +364,12 @@ public class NicDaoImpl extends GenericDaoBase<NicVO, Long> implements NicDao {
         sc.setParameters("address", "%" + keyword + "%");
         return listBy(sc);
     }
+
+    @Override
+    public NicVO findByInstanceIdAndMacAddress(long instanceId, String macAddress) {
+        SearchCriteria<NicVO> sc = AllFieldsSearch.create();
+        sc.setParameters("instance", instanceId);
+        sc.setParameters("macAddress", macAddress);
+        return findOneBy(sc);
+    }
 }
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java
index fad9747..2052fd2 100755
--- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java
@@ -86,6 +86,8 @@ public interface VMInstanceDao extends GenericDao<VMInstanceVO, Long>, StateDao<
 
     VMInstanceVO findVMByInstanceName(String name);
 
+    VMInstanceVO findVMByInstanceNameIncludingRemoved(String name);
+
     VMInstanceVO findVMByHostName(String hostName);
 
     void updateProxyId(long id, Long proxyId, Date time);
@@ -114,6 +116,8 @@ public interface VMInstanceDao extends GenericDao<VMInstanceVO, Long>, StateDao<
 
     List<VMInstanceVO> listVmsMigratingFromHost(Long hostId);
 
+    List<VMInstanceVO> listByZoneWithBackups(Long zoneId, Long backupOfferingId);
+
     public Long countActiveByHostId(long hostId);
 
     Pair<List<Long>, Map<Long, Double>> listClusterIdsInZoneByVmCount(long zoneId, long accountId);
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java
index 3af74ab..1d7d444 100755
--- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java
@@ -94,6 +94,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
     protected SearchBuilder<VMInstanceVO> HostAndStateSearch;
     protected SearchBuilder<VMInstanceVO> StartingWithNoHostSearch;
     protected SearchBuilder<VMInstanceVO> NotMigratingSearch;
+    protected SearchBuilder<VMInstanceVO> BackupSearch;
     protected SearchBuilder<VMInstanceVO> LastHostAndStatesSearch;
 
     @Inject
@@ -288,6 +289,12 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
         NotMigratingSearch.and("state", NotMigratingSearch.entity().getState(), Op.NEQ);
         NotMigratingSearch.done();
 
+        BackupSearch = createSearchBuilder();
+        BackupSearch.and("zone_id", BackupSearch.entity().getDataCenterId(), Op.EQ);
+        BackupSearch.and("backup_offering_not_null", BackupSearch.entity().getBackupOfferingId(), Op.NNULL);
+        BackupSearch.and("backup_offering_id", BackupSearch.entity().getBackupOfferingId(), Op.EQ);
+        BackupSearch.done();
+
         LastHostAndStatesSearch = createSearchBuilder();
         LastHostAndStatesSearch.and("lastHost", LastHostAndStatesSearch.entity().getLastHostId(), Op.EQ);
         LastHostAndStatesSearch.and("states", LastHostAndStatesSearch.entity().getState(), Op.IN);
@@ -457,6 +464,13 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
     }
 
     @Override
+    public VMInstanceVO findVMByInstanceNameIncludingRemoved(String name) {
+        SearchCriteria<VMInstanceVO> sc = InstanceNameSearch.create();
+        sc.setParameters("instanceName", name);
+        return findOneIncludingRemovedBy(sc);
+    }
+
+    @Override
     public VMInstanceVO findVMByHostName(String hostName) {
         SearchCriteria<VMInstanceVO> sc = HostNameSearch.create();
         sc.setParameters("hostName", hostName);
@@ -592,6 +606,16 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
     }
 
     @Override
+    public List<VMInstanceVO> listByZoneWithBackups(Long zoneId, Long backupOfferingId) {
+        SearchCriteria<VMInstanceVO> sc = BackupSearch.create();
+        sc.setParameters("zone_id", zoneId);
+        if (backupOfferingId != null) {
+            sc.setParameters("backup_offering_id", backupOfferingId);
+        }
+        return listBy(sc);
+    }
+
+    @Override
     public Long countActiveByHostId(long hostId) {
         SearchCriteria<Long> sc = CountActiveByHost.create();
         sc.setParameters("host", hostId);
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingVO.java
new file mode 100644
index 0000000..c5d8790
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingVO.java
@@ -0,0 +1,126 @@
+// 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;
+
+import java.util.Date;
+import java.util.UUID;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+@Entity
+@Table(name = "backup_offering")
+public class BackupOfferingVO implements BackupOffering {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private long id;
+
+    @Column(name = "uuid")
+    private String uuid;
+
+    @Column(name = "name")
+    private String name;
+
+    @Column(name = "description")
+    private String description;
+
+    @Column(name = "external_id")
+    private String externalId;
+
+    @Column(name = "zone_id")
+    private long zoneId;
+
+    @Column(name = "user_driven_backup")
+    private boolean userDrivenBackupAllowed;
+
+    @Column(name = "provider")
+    private String provider;
+
+    @Column(name = "created")
+    @Temporal(value = TemporalType.TIMESTAMP)
+    private Date created;
+
+    @Column(name = "removed")
+    @Temporal(value = TemporalType.TIMESTAMP)
+    private Date removed;
+
+    public BackupOfferingVO() {
+        this.uuid = UUID.randomUUID().toString();
+    }
+
+    public BackupOfferingVO(final long zoneId, final String externalId, final String provider, final String name, final String description, final boolean userDrivenBackupAllowed) {
+        this();
+        this.name = name;
+        this.description = description;
+        this.zoneId = zoneId;
+        this.provider = provider;
+        this.externalId = externalId;
+        this.userDrivenBackupAllowed = userDrivenBackupAllowed;
+        this.created = new Date();
+    }
+
+    public String getUuid() {
+        return uuid;
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getExternalId() {
+        return externalId;
+    }
+
+    @Override
+    public long getZoneId() {
+        return zoneId;
+    }
+
+    @Override
+    public boolean isUserDrivenBackupAllowed() {
+        return userDrivenBackupAllowed;
+    }
+
+    public void setUserDrivenBackupAllowed(boolean userDrivenBackupAllowed) {
+        this.userDrivenBackupAllowed = userDrivenBackupAllowed;
+    }
+
+    @Override
+    public String getProvider() {
+        return provider;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public Date getCreated() {
+        return created;
+    }
+}
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java
new file mode 100644
index 0000000..ba31dc5
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java
@@ -0,0 +1,124 @@
+// 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;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import com.cloud.utils.DateUtil;
+
+@Entity
+@Table(name = "backup_schedule")
+public class BackupScheduleVO implements BackupSchedule {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private long id;
+
+    @Column(name = "vm_id")
+    private Long vmId;
+
+    @Column(name = "schedule_type")
+    private Short scheduleType;
+
+    @Column(name = "schedule")
+    String schedule;
+
+    @Column(name = "timezone")
+    String timezone;
+
+    @Column(name = "scheduled_timestamp")
+    @Temporal(value = TemporalType.TIMESTAMP)
+    Date scheduledTimestamp;
+
+    @Column(name = "async_job_id")
+    Long asyncJobId;
+
+    public BackupScheduleVO() {
+    }
+
+    public BackupScheduleVO(Long vmId, DateUtil.IntervalType scheduleType, String schedule, String timezone, Date scheduledTimestamp) {
+        this.vmId = vmId;
+        this.scheduleType = (short) scheduleType.ordinal();
+        this.schedule = schedule;
+        this.timezone = timezone;
+        this.scheduledTimestamp = scheduledTimestamp;
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    public Long getVmId() {
+        return vmId;
+    }
+
+    public void setVmId(Long vmId) {
+        this.vmId = vmId;
+    }
+
+    @Override
+    public DateUtil.IntervalType getScheduleType() {
+        return scheduleType == null ? null : DateUtil.getIntervalType(scheduleType);
+    }
+
+    public void setScheduleType(Short intervalType) {
+        this.scheduleType = intervalType;
+    }
+
+    public String getSchedule() {
+        return schedule;
+    }
+
+    public void setSchedule(String schedule) {
+        this.schedule = schedule;
+    }
+
+    public String getTimezone() {
+        return timezone;
+    }
+
+    public void setTimezone(String timezone) {
+        this.timezone = timezone;
+    }
+
+    public Date getScheduledTimestamp() {
+        return scheduledTimestamp;
+    }
+
+    public void setScheduledTimestamp(Date scheduledTimestamp) {
+        this.scheduledTimestamp = scheduledTimestamp;
+    }
+
+    public Long getAsyncJobId() {
+        return asyncJobId;
+    }
+
+    public void setAsyncJobId(Long asyncJobId) {
+        this.asyncJobId = asyncJobId;
+    }
+}
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java
new file mode 100644
index 0000000..e56f55c
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java
@@ -0,0 +1,190 @@
+//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
+//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;
+
+import java.util.UUID;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "backups")
+public class BackupVO implements Backup {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private long id;
+
+    @Column(name = "uuid")
+    private String uuid;
+
+    @Column(name = "vm_id")
+    private long vmId;
+
+    @Column(name = "external_id")
+    private String externalId;
+
+    @Column(name = "type")
+    private String backupType;
+
+    @Column(name = "date")
+    private String date;
+
+    @Column(name = "size")
+    private Long size;
+
+    @Column(name = "protected_size")
+    private Long protectedSize;
+
+    @Enumerated(value = EnumType.STRING)
+    @Column(name = "status")
+    private Backup.Status status;
+
+    @Column(name = "backup_offering_id")
+    private long backupOfferingId;
+
+    @Column(name = "account_id")
+    private long accountId;
+
+    @Column(name = "domain_id")
+    private long domainId;
+
+    @Column(name = "zone_id")
+    private long zoneId;
+
+    public BackupVO() {
+        this.uuid = UUID.randomUUID().toString();
+    }
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+    @Override
+    public String getUuid() {
+        return uuid;
+    }
+
+    @Override
+    public long getVmId() {
+        return vmId;
+    }
+
+    public void setVmId(long vmId) {
+        this.vmId = vmId;
+    }
+
+    @Override
+    public String getExternalId() {
+        return externalId;
+    }
+
+    public void setExternalId(String externalId) {
+        this.externalId = externalId;
+    }
+
+    public String getType() {
+        return backupType;
+    }
+
+    public void setType(String type) {
+        this.backupType = type;
+    }
+
+    @Override
+    public String getDate() {
+        return date;
+    }
+
+    public void setDate(String date) {
+        this.date = date;
+    }
+
+    @Override
+    public Long getSize() {
+        return size;
+    }
+
+    public void setSize(Long size) {
+        this.size = size;
+    }
+
+    @Override
+    public Long getProtectedSize() {
+        return protectedSize;
+    }
+
+    public void setProtectedSize(Long protectedSize) {
+        this.protectedSize = protectedSize;
+    }
+
+    @Override
+    public Status getStatus() {
+        return status;
+    }
+
+    public void setStatus(Status status) {
+        this.status = status;
+    }
+
+    public long getBackupOfferingId() {
+        return backupOfferingId;
+    }
+
+    public void setBackupOfferingId(long backupOfferingId) {
+        this.backupOfferingId = backupOfferingId;
+    }
+
+    @Override
+    public long getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(long accountId) {
+        this.accountId = accountId;
+    }
+
+    @Override
+    public long getDomainId() {
+        return domainId;
+    }
+
+    public void setDomainId(long domainId) {
+        this.domainId = domainId;
+    }
+
+    public long getZoneId() {
+        return zoneId;
+    }
+
+    public void setZoneId(long zoneId) {
+        this.zoneId = zoneId;
+    }
+
+    @Override
+    public Class<?> getEntityType() {
+        return Backup.class;
+    }
+}
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java
similarity index 56%
copy from engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
copy to engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java
index 89e2c83..5d2f5ac 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java
@@ -14,21 +14,27 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.storage.dao;
 
-import java.util.List;
+package org.apache.cloudstack.backup.dao;
 
-import com.cloud.storage.DiskOfferingVO;
-import com.cloud.utils.db.GenericDao;
+import java.util.List;
 
-public interface DiskOfferingDao extends GenericDao<DiskOfferingVO, Long> {
+import org.apache.cloudstack.api.response.BackupResponse;
+import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.backup.BackupVO;
 
-    List<DiskOfferingVO> findPrivateDiskOffering();
+import com.cloud.utils.db.GenericDao;
 
-    List<DiskOfferingVO> findPublicDiskOfferings();
+public interface BackupDao extends GenericDao<BackupVO, Long> {
 
-    DiskOfferingVO findByUniqueName(String uniqueName);
+    Backup findByVmId(Long vmId);
+    Backup findByVmIdIncludingRemoved(Long vmId);
 
-    DiskOfferingVO persistDeafultDiskOffering(DiskOfferingVO offering);
+    List<Backup> listByVmId(Long zoneId, Long vmId);
+    List<Backup> listByAccountId(Long accountId);
+    List<Backup> listByOfferingId(Long offeringId);
+    List<Backup> syncBackups(Long zoneId, Long vmId, List<Backup> externalBackups);
+    BackupVO getBackupVO(Backup backup);
 
+    BackupResponse newBackupResponse(Backup backup);
 }
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java
new file mode 100644
index 0000000..fefbb68
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java
@@ -0,0 +1,172 @@
+// 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.dao;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.apache.cloudstack.api.response.BackupResponse;
+import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.backup.BackupVO;
+
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.domain.DomainVO;
+import com.cloud.domain.dao.DomainDao;
+import com.cloud.user.AccountVO;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.VMInstanceDao;
+import com.google.gson.Gson;
+
+public class BackupDaoImpl extends GenericDaoBase<BackupVO, Long> implements BackupDao {
+
+    @Inject
+    AccountDao accountDao;
+
+    @Inject
+    DomainDao domainDao;
+
+    @Inject
+    DataCenterDao dataCenterDao;
+
+    @Inject
+    VMInstanceDao vmInstanceDao;
+
+    @Inject
+    BackupOfferingDao backupOfferingDao;
+
+    private SearchBuilder<BackupVO> backupSearch;
+
+    public BackupDaoImpl() {
+    }
+
+    @PostConstruct
+    protected void init() {
+        backupSearch = createSearchBuilder();
+        backupSearch.and("vm_id", backupSearch.entity().getVmId(), SearchCriteria.Op.EQ);
+        backupSearch.and("external_id", backupSearch.entity().getExternalId(), SearchCriteria.Op.EQ);
+        backupSearch.done();
+    }
+
+    @Override
+    public List<Backup> listByAccountId(Long accountId) {
+        SearchCriteria<BackupVO> sc = backupSearch.create();
+        sc.setParameters("account_id", accountId);
+        return new ArrayList<>(listBy(sc));
+    }
+
+    @Override
+    public Backup findByVmId(Long vmId) {
+        SearchCriteria<BackupVO> sc = backupSearch.create();
+        sc.setParameters("vm_id", vmId);
+        return findOneBy(sc);
+    }
+
+    @Override
+    public Backup findByVmIdIncludingRemoved(Long vmId) {
+        SearchCriteria<BackupVO> sc = backupSearch.create();
+        sc.setParameters("vm_id", vmId);
+        return findOneIncludingRemovedBy(sc);
+    }
+
+    @Override
+    public List<Backup> listByVmId(Long zoneId, Long vmId) {
+        SearchCriteria<BackupVO> sc = backupSearch.create();
+        sc.setParameters("vm_id", vmId);
+        if (zoneId != null) {
+            sc.setParameters("zone_id", zoneId);
+        }
+        return new ArrayList<>(listBy(sc));
+    }
+
+    @Override
+    public List<Backup> listByOfferingId(Long offeringId) {
+        SearchCriteria<BackupVO> sc = backupSearch.create();
+        sc.setParameters("offering_id", offeringId);
+        return new ArrayList<>(listBy(sc));
+    }
+
+    private Backup findByExternalId(Long zoneId, String externalId) {
+        SearchCriteria<BackupVO> sc = backupSearch.create();
+        sc.setParameters("external_id", externalId);
+        sc.setParameters("zone_id", zoneId);
+        return findOneBy(sc);
+    }
+
+    public BackupVO getBackupVO(Backup backup) {
+        BackupVO backupVO = new BackupVO();
+        backupVO.setExternalId(backup.getExternalId());
+        backupVO.setVmId(backup.getVmId());
+        return backupVO;
+    }
+
+    public void removeExistingBackups(Long zoneId, Long vmId) {
+        SearchCriteria<BackupVO> sc = backupSearch.create();
+        sc.setParameters("vm_id", vmId);
+        sc.setParameters("zone_id", zoneId);
+        expunge(sc);
+    }
+
+    @Override
+    public List<Backup> syncBackups(Long zoneId, Long vmId, List<Backup> externalBackups) {
+        for (Backup backup : externalBackups) {
+            BackupVO backupVO = getBackupVO(backup);
+            persist(backupVO);
+        }
+        return listByVmId(zoneId, vmId);
+    }
+
+    @Override
+    public BackupResponse newBackupResponse(Backup backup) {
+        VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId());
+        AccountVO account = accountDao.findByIdIncludingRemoved(vm.getAccountId());
+        DomainVO domain = domainDao.findByIdIncludingRemoved(vm.getDomainId());
+        DataCenterVO zone = dataCenterDao.findByIdIncludingRemoved(vm.getDataCenterId());
+        BackupOffering offering = backupOfferingDao.findByIdIncludingRemoved(vm.getBackupOfferingId());
+
+        BackupResponse response = new BackupResponse();
+        response.setId(backup.getUuid());
+        response.setVmId(vm.getUuid());
+        response.setVmName(vm.getHostName());
+        response.setExternalId(backup.getExternalId());
+        response.setType(backup.getType());
+        response.setDate(backup.getDate());
+        response.setSize(backup.getSize());
+        response.setProtectedSize(backup.getProtectedSize());
+        response.setStatus(backup.getStatus());
+        response.setVolumes(new Gson().toJson(vm.getBackupVolumeList().toArray(), Backup.VolumeInfo[].class));
+        response.setBackupOfferingId(offering.getUuid());
+        response.setBackupOffering(offering.getName());
+        response.setAccountId(account.getUuid());
+        response.setAccount(account.getAccountName());
+        response.setDomainId(domain.getUuid());
+        response.setDomain(domain.getName());
+        response.setZoneId(zone.getUuid());
+        response.setZone(zone.getName());
+        response.setObjectName("backup");
+        return response;
+    }
+}
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java
similarity index 62%
copy from engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
copy to engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java
index 89e2c83..d001de8 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDao.java
@@ -14,21 +14,17 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.storage.dao;
 
-import java.util.List;
+package org.apache.cloudstack.backup.dao;
 
-import com.cloud.storage.DiskOfferingVO;
-import com.cloud.utils.db.GenericDao;
-
-public interface DiskOfferingDao extends GenericDao<DiskOfferingVO, Long> {
-
-    List<DiskOfferingVO> findPrivateDiskOffering();
+import org.apache.cloudstack.api.response.BackupOfferingResponse;
+import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.backup.BackupOfferingVO;
 
-    List<DiskOfferingVO> findPublicDiskOfferings();
-
-    DiskOfferingVO findByUniqueName(String uniqueName);
-
-    DiskOfferingVO persistDeafultDiskOffering(DiskOfferingVO offering);
+import com.cloud.utils.db.GenericDao;
 
+public interface BackupOfferingDao extends GenericDao<BackupOfferingVO, Long> {
+    BackupOfferingResponse newBackupOfferingResponse(BackupOffering policy);
+    BackupOffering findByExternalId(String externalId, Long zoneId);
+    BackupOffering findByName(String name, Long zoneId);
 }
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java
new file mode 100644
index 0000000..0568a01
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java
@@ -0,0 +1,88 @@
+// 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.dao;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.apache.cloudstack.api.response.BackupOfferingResponse;
+import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.backup.BackupOfferingVO;
+
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+
+public class BackupOfferingDaoImpl extends GenericDaoBase<BackupOfferingVO, Long> implements BackupOfferingDao {
+
+    @Inject
+    DataCenterDao dataCenterDao;
+
+    private SearchBuilder<BackupOfferingVO> backupPoliciesSearch;
+
+    public BackupOfferingDaoImpl() {
+    }
+
+    @PostConstruct
+    protected void init() {
+        backupPoliciesSearch = createSearchBuilder();
+        backupPoliciesSearch.and("name", backupPoliciesSearch.entity().getName(), SearchCriteria.Op.EQ);
+        backupPoliciesSearch.and("zone_id", backupPoliciesSearch.entity().getZoneId(), SearchCriteria.Op.EQ);
+        backupPoliciesSearch.and("external_id", backupPoliciesSearch.entity().getExternalId(), SearchCriteria.Op.EQ);
+        backupPoliciesSearch.done();
+    }
+
+    @Override
+    public BackupOfferingResponse newBackupOfferingResponse(BackupOffering offering) {
+        DataCenterVO zone = dataCenterDao.findById(offering.getZoneId());
+
+        BackupOfferingResponse response = new BackupOfferingResponse();
+        response.setId(offering.getUuid());
+        response.setName(offering.getName());
+        response.setDescription(offering.getDescription());
+        response.setExternalId(offering.getExternalId());
+        response.setUserDrivenBackups(offering.isUserDrivenBackupAllowed());
+        if (zone != null) {
+            response.setZoneId(zone.getUuid());
+            response.setZoneName(zone.getName());
+        }
+        response.setCreated(offering.getCreated());
+        response.setObjectName("backupoffering");
+        return response;
+    }
+
+    @Override
+    public BackupOffering findByExternalId(String externalId, Long zoneId) {
+        SearchCriteria<BackupOfferingVO> sc = backupPoliciesSearch.create();
+        sc.setParameters("external_id", externalId);
+        if (zoneId != null) {
+            sc.setParameters("zone_id", zoneId);
+        }
+        return findOneBy(sc);
+    }
+
+    @Override
+    public BackupOffering findByName(String name, Long zoneId) {
+        SearchCriteria<BackupOfferingVO> sc = backupPoliciesSearch.create();
+        sc.setParameters("name", name);
+        sc.setParameters("zone_id", zoneId);
+        return findOneBy(sc);
+    }
+}
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDao.java
similarity index 63%
copy from engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
copy to engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDao.java
index 89e2c83..516b011 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDao.java
@@ -14,21 +14,22 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.storage.dao;
 
-import java.util.List;
-
-import com.cloud.storage.DiskOfferingVO;
-import com.cloud.utils.db.GenericDao;
+package org.apache.cloudstack.backup.dao;
 
-public interface DiskOfferingDao extends GenericDao<DiskOfferingVO, Long> {
+import java.util.Date;
+import java.util.List;
 
-    List<DiskOfferingVO> findPrivateDiskOffering();
+import org.apache.cloudstack.api.response.BackupScheduleResponse;
+import org.apache.cloudstack.backup.BackupSchedule;
+import org.apache.cloudstack.backup.BackupScheduleVO;
 
-    List<DiskOfferingVO> findPublicDiskOfferings();
+import com.cloud.utils.db.GenericDao;
 
-    DiskOfferingVO findByUniqueName(String uniqueName);
+public interface BackupScheduleDao extends GenericDao<BackupScheduleVO, Long> {
+    BackupScheduleVO findByVM(Long vmId);
 
-    DiskOfferingVO persistDeafultDiskOffering(DiskOfferingVO offering);
+    List<BackupScheduleVO> getSchedulesToExecute(Date currentTimestamp);
 
+    BackupScheduleResponse newBackupScheduleResponse(BackupSchedule schedule);
 }
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java
new file mode 100644
index 0000000..7a58679
--- /dev/null
+++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupScheduleDaoImpl.java
@@ -0,0 +1,86 @@
+// 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.dao;
+
+import java.util.Date;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.apache.cloudstack.api.response.BackupScheduleResponse;
+import org.apache.cloudstack.backup.BackupSchedule;
+import org.apache.cloudstack.backup.BackupScheduleVO;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.VMInstanceDao;
+
+public class BackupScheduleDaoImpl extends GenericDaoBase<BackupScheduleVO, Long> implements BackupScheduleDao {
+
+    @Inject
+    VMInstanceDao vmInstanceDao;
+
+    private SearchBuilder<BackupScheduleVO> backupScheduleSearch;
+    private SearchBuilder<BackupScheduleVO> executableSchedulesSearch;
+
+    public BackupScheduleDaoImpl() {
+    }
+
+    @PostConstruct
+    protected void init() {
+        backupScheduleSearch = createSearchBuilder();
+        backupScheduleSearch.and("vm_id", backupScheduleSearch.entity().getVmId(), SearchCriteria.Op.EQ);
+        backupScheduleSearch.and("async_job_id", backupScheduleSearch.entity().getAsyncJobId(), SearchCriteria.Op.EQ);
+        backupScheduleSearch.done();
+
+        executableSchedulesSearch = createSearchBuilder();
+        executableSchedulesSearch.and("scheduledTimestamp", executableSchedulesSearch.entity().getScheduledTimestamp(), SearchCriteria.Op.LT);
+        executableSchedulesSearch.and("asyncJobId", executableSchedulesSearch.entity().getAsyncJobId(), SearchCriteria.Op.NULL);
+        executableSchedulesSearch.done();
+    }
+
+    @Override
+    public BackupScheduleVO findByVM(Long vmId) {
+        SearchCriteria<BackupScheduleVO> sc = backupScheduleSearch.create();
+        sc.setParameters("vm_id", vmId);
+        return findOneBy(sc);
+    }
+
+    @Override
+    public List<BackupScheduleVO> getSchedulesToExecute(Date currentTimestamp) {
+        SearchCriteria<BackupScheduleVO> sc = executableSchedulesSearch.create();
+        sc.setParameters("scheduledTimestamp", currentTimestamp);
+        return listBy(sc);
+    }
+
+    @Override
+    public BackupScheduleResponse newBackupScheduleResponse(BackupSchedule schedule) {
+        VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(schedule.getVmId());
+        BackupScheduleResponse response = new BackupScheduleResponse();
+        response.setVmId(vm.getUuid());
+        response.setVmName(vm.getHostName());
+        response.setIntervalType(schedule.getScheduleType());
+        response.setSchedule(schedule.getSchedule());
+        response.setTimezone(schedule.getTimezone());
+        response.setObjectName("backupschedule");
+        return response;
+    }
+}
diff --git a/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java b/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java
index cdb80fe..4ab5f42 100644
--- a/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java
@@ -16,13 +16,12 @@
 // under the License.
 package org.apache.cloudstack.engine.cloud.entity.api.db;
 
-import com.cloud.hypervisor.Hypervisor.HypervisorType;
-import com.cloud.utils.db.Encrypt;
-import com.cloud.utils.db.GenericDao;
-import com.cloud.utils.db.StateMachine;
-import com.cloud.utils.fsm.FiniteStateObject;
-import com.cloud.vm.VirtualMachine;
-import com.cloud.vm.VirtualMachine.State;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 
 import javax.persistence.Column;
 import javax.persistence.DiscriminatorColumn;
@@ -38,11 +37,17 @@ import javax.persistence.TableGenerator;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 import javax.persistence.Transient;
-import java.security.SecureRandom;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
+
+import org.apache.cloudstack.backup.Backup;
+
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.utils.db.Encrypt;
+import com.cloud.utils.db.GenericDao;
+import com.cloud.utils.db.StateMachine;
+import com.cloud.utils.fsm.FiniteStateObject;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachine.State;
+import com.google.gson.Gson;
 
 @Entity
 @Table(name = "vm_instance")
@@ -175,6 +180,15 @@ public class VMEntityVO implements VirtualMachine, FiniteStateObject<State, Virt
     @Column(name = "display_vm", updatable = true, nullable = false)
     protected boolean display = true;
 
+    @Column(name = "backup_offering_id")
+    protected Long backupOfferingId;
+
+    @Column(name = "backup_external_id")
+    private String backupExternalId;
+
+    @Column(name = "backup_volumes")
+    private String backupVolumes;
+
     @Transient
     private VMReservationVO vmReservation;
 
@@ -555,4 +569,19 @@ public class VMEntityVO implements VirtualMachine, FiniteStateObject<State, Virt
     public PartitionType partitionType() {
         return PartitionType.VM;
     }
+
+    @Override
+    public Long getBackupOfferingId() {
+        return backupOfferingId;
+    }
+
+    @Override
+    public String getBackupExternalId() {
+        return backupExternalId;
+    }
+
+    @Override
+    public List<Backup.VolumeInfo> getBackupVolumeList() {
+        return Arrays.asList(new Gson().fromJson(this.backupVolumes, Backup.VolumeInfo[].class));
+    }
 }
diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
index 34c356d..7faf85c 100644
--- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
+++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
@@ -218,6 +218,7 @@
   <bean id="usageVPNUserDaoImpl" class="com.cloud.usage.dao.UsageVPNUserDaoImpl" />
   <bean id="usageVolumeDaoImpl" class="com.cloud.usage.dao.UsageVolumeDaoImpl" />
   <bean id="usageVmDiskDaoImpl" class="com.cloud.usage.dao.UsageVmDiskDaoImpl" />
+  <bean id="usageBackupDaoImpl" class="com.cloud.usage.dao.UsageBackupDaoImpl" />
   <bean id="userAccountDaoImpl" class="com.cloud.user.dao.UserAccountDaoImpl" />
   <bean id="userAccountJoinDaoImpl" class="com.cloud.api.query.dao.UserAccountJoinDaoImpl" />
   <bean id="userIpv6AddressDaoImpl" class="com.cloud.network.dao.UserIpv6AddressDaoImpl" />
@@ -285,6 +286,9 @@
   <bean id="outOfBandManagementDaoImpl" class="org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDaoImpl" />
   <bean id="GuestOsDetailsDaoImpl" class="org.apache.cloudstack.resourcedetail.dao.GuestOsDetailsDaoImpl" />
   <bean id="annotationDaoImpl" class="org.apache.cloudstack.annotation.dao.AnnotationDaoImpl" />
+  <bean id="backupOfferingDaoImpl" class="org.apache.cloudstack.backup.dao.BackupOfferingDaoImpl" />
+  <bean id="backupScheduleDaoImpl" class="org.apache.cloudstack.backup.dao.BackupScheduleDaoImpl" />
+  <bean id="backupDaoImpl" class="org.apache.cloudstack.backup.dao.BackupDaoImpl" />
   <bean id="directDownloadCertificateDaoImpl" class="org.apache.cloudstack.direct.download.DirectDownloadCertificateDaoImpl" />
   <bean id="directDownloadCertificateHostMapDaoImpl" class="org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMapDaoImpl" />
   <bean id="templateOVFPropertiesDaoImpl" class="com.cloud.storage.dao.TemplateOVFPropertiesDaoImpl" />
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41300to41400.sql b/engine/schema/src/main/resources/META-INF/db/schema-41300to41400.sql
index ad257bc..db7482b 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41300to41400.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41300to41400.sql
@@ -28,6 +28,256 @@ UPDATE `cloud`.`vm_template` SET guest_os_id=99 WHERE id=1;
 -- #3659 Fix typo: the past tense of shutdown is shutdown, not shutdowned
 UPDATE `cloud`.`vm_instance` SET state='Shutdown' WHERE state='Shutdowned';
 
+-- Backup and Recovery
+
+CREATE TABLE IF NOT EXISTS `cloud`.`backup_offering` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `uuid` varchar(40) NOT NULL UNIQUE,
+  `name` varchar(255) NOT NULL COMMENT 'backup offering name',
+  `description` varchar(255) NOT NULL COMMENT 'backup offering description',
+  `external_id` varchar(255) DEFAULT NULL COMMENT 'external ID on provider side',
+  `user_driven_backup` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'whether user can do adhoc backups and backup schedules allowed, default false',
+  `zone_id` bigint(20) unsigned NOT NULL COMMENT 'zone id',
+  `provider` varchar(255) NOT NULL COMMENT 'backup provider',
+  `created` datetime DEFAULT NULL,
+  `removed` datetime DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  CONSTRAINT `fk_backup_offering__zone_id` FOREIGN KEY (`zone_id`) REFERENCES `data_center` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `backup_offering_id` bigint unsigned DEFAULT NULL COMMENT 'ID of backup offering';
+ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `backup_external_id` varchar(255) DEFAULT NULL COMMENT 'ID of external backup job or container if any';
+ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `backup_volumes` text DEFAULT NULL COMMENT 'details of backedup volumes';
+
+CREATE TABLE IF NOT EXISTS `cloud`.`backups` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `uuid` varchar(40) NOT NULL UNIQUE,
+  `vm_id` bigint(20) unsigned NOT NULL,
+  `external_id` varchar(255) DEFAULT NULL COMMENT 'external ID',
+  `type` varchar(255) NOT NULL COMMENT 'backup type',
+  `date` varchar(255) NOT NULL COMMENT 'backup date',
+  `size` bigint(20) DEFAULT 0 COMMENT 'size of the backup',
+  `protected_size` bigint(20) DEFAULT 0,
+  `status` varchar(32) DEFAULT NULL,
+  `backup_offering_id` bigint(20) unsigned NOT NULL,
+  `account_id` bigint(20) unsigned NOT NULL,
+  `domain_id` bigint(20) unsigned NOT NULL,
+  `zone_id` bigint(20) unsigned NOT NULL,
+  `removed` datetime DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  CONSTRAINT `fk_backup__vm_id` FOREIGN KEY (`vm_id`) REFERENCES `vm_instance` (`id`) ON DELETE CASCADE,
+  CONSTRAINT `fk_backup__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `cloud`.`backup_schedule` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `vm_id` bigint(20) unsigned NOT NULL UNIQUE,
+  `schedule_type` int(4) DEFAULT NULL COMMENT 'backup schedulet type e.g. hourly, daily, etc.',
+  `schedule` varchar(100) DEFAULT NULL COMMENT 'schedule time of execution',
+  `timezone` varchar(100) DEFAULT NULL COMMENT 'the timezone in which the schedule time is specified',
+  `scheduled_timestamp` datetime DEFAULT NULL COMMENT 'Time at which the backup was scheduled for execution',
+  `async_job_id` bigint(20) unsigned DEFAULT NULL COMMENT 'If this schedule is being executed, it is the id of the create aysnc_job. Before that it is null',
+  PRIMARY KEY (`id`),
+  CONSTRAINT `fk_backup_schedule__vm_id` FOREIGN KEY (`vm_id`) REFERENCES `vm_instance` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `cloud_usage`.`usage_backup` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `zone_id` bigint(20) unsigned NOT NULL,
+  `account_id` bigint(20) unsigned NOT NULL,
+  `domain_id` bigint(20) unsigned NOT NULL,
+  `vm_id` bigint(20) unsigned NOT NULL,
+  `backup_offering_id` bigint(20) unsigned NOT NULL,
+  `size` bigint(20) DEFAULT 0,
+  `protected_size` bigint(20) DEFAULT 0,
+  `created` datetime NOT NULL,
+  `removed` datetime DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  INDEX `i_usage_backup` (`zone_id`,`account_id`,`vm_id`,`created`)
+) ENGINE=InnoDB CHARSET=utf8;
+
+DROP VIEW IF EXISTS `cloud`.`user_vm_view`;
+CREATE
+VIEW `user_vm_view` AS
+    SELECT
+        `vm_instance`.`id` AS `id`,
+        `vm_instance`.`name` AS `name`,
+        `user_vm`.`display_name` AS `display_name`,
+        `user_vm`.`user_data` AS `user_data`,
+        `account`.`id` AS `account_id`,
+        `account`.`uuid` AS `account_uuid`,
+        `account`.`account_name` AS `account_name`,
+        `account`.`type` AS `account_type`,
+        `domain`.`id` AS `domain_id`,
+        `domain`.`uuid` AS `domain_uuid`,
+        `domain`.`name` AS `domain_name`,
+        `domain`.`path` AS `domain_path`,
+        `projects`.`id` AS `project_id`,
+        `projects`.`uuid` AS `project_uuid`,
+        `projects`.`name` AS `project_name`,
+        `instance_group`.`id` AS `instance_group_id`,
+        `instance_group`.`uuid` AS `instance_group_uuid`,
+        `instance_group`.`name` AS `instance_group_name`,
+        `vm_instance`.`uuid` AS `uuid`,
+        `vm_instance`.`user_id` AS `user_id`,
+        `vm_instance`.`last_host_id` AS `last_host_id`,
+        `vm_instance`.`vm_type` AS `type`,
+        `vm_instance`.`limit_cpu_use` AS `limit_cpu_use`,
+        `vm_instance`.`created` AS `created`,
+        `vm_instance`.`state` AS `state`,
+        `vm_instance`.`removed` AS `removed`,
+        `vm_instance`.`ha_enabled` AS `ha_enabled`,
+        `vm_instance`.`hypervisor_type` AS `hypervisor_type`,
+        `vm_instance`.`instance_name` AS `instance_name`,
+        `vm_instance`.`guest_os_id` AS `guest_os_id`,
+        `vm_instance`.`display_vm` AS `display_vm`,
+        `guest_os`.`uuid` AS `guest_os_uuid`,
+        `vm_instance`.`pod_id` AS `pod_id`,
+        `host_pod_ref`.`uuid` AS `pod_uuid`,
+        `vm_instance`.`private_ip_address` AS `private_ip_address`,
+        `vm_instance`.`private_mac_address` AS `private_mac_address`,
+        `vm_instance`.`vm_type` AS `vm_type`,
+        `data_center`.`id` AS `data_center_id`,
+        `data_center`.`uuid` AS `data_center_uuid`,
+        `data_center`.`name` AS `data_center_name`,
+        `data_center`.`is_security_group_enabled` AS `security_group_enabled`,
+        `data_center`.`networktype` AS `data_center_type`,
+        `host`.`id` AS `host_id`,
+        `host`.`uuid` AS `host_uuid`,
+        `host`.`name` AS `host_name`,
+        `vm_template`.`id` AS `template_id`,
+        `vm_template`.`uuid` AS `template_uuid`,
+        `vm_template`.`name` AS `template_name`,
+        `vm_template`.`display_text` AS `template_display_text`,
+        `vm_template`.`enable_password` AS `password_enabled`,
+        `iso`.`id` AS `iso_id`,
+        `iso`.`uuid` AS `iso_uuid`,
+        `iso`.`name` AS `iso_name`,
+        `iso`.`display_text` AS `iso_display_text`,
+        `service_offering`.`id` AS `service_offering_id`,
+        `svc_disk_offering`.`uuid` AS `service_offering_uuid`,
+        `disk_offering`.`uuid` AS `disk_offering_uuid`,
+        `disk_offering`.`id` AS `disk_offering_id`,
+        (CASE
+            WHEN ISNULL(`service_offering`.`cpu`) THEN `custom_cpu`.`value`
+            ELSE `service_offering`.`cpu`
+        END) AS `cpu`,
+        (CASE
+            WHEN ISNULL(`service_offering`.`speed`) THEN `custom_speed`.`value`
+            ELSE `service_offering`.`speed`
+        END) AS `speed`,
+        (CASE
+            WHEN ISNULL(`service_offering`.`ram_size`) THEN `custom_ram_size`.`value`
+            ELSE `service_offering`.`ram_size`
+        END) AS `ram_size`,
+        `backup_offering`.`uuid` AS `backup_offering_uuid`,
+        `backup_offering`.`id` AS `backup_offering_id`,
+        `svc_disk_offering`.`name` AS `service_offering_name`,
+        `disk_offering`.`name` AS `disk_offering_name`,
+        `backup_offering`.`name` AS `backup_offering_name`,
+        `storage_pool`.`id` AS `pool_id`,
+        `storage_pool`.`uuid` AS `pool_uuid`,
+        `storage_pool`.`pool_type` AS `pool_type`,
+        `volumes`.`id` AS `volume_id`,
+        `volumes`.`uuid` AS `volume_uuid`,
+        `volumes`.`device_id` AS `volume_device_id`,
+        `volumes`.`volume_type` AS `volume_type`,
+        `security_group`.`id` AS `security_group_id`,
+        `security_group`.`uuid` AS `security_group_uuid`,
+        `security_group`.`name` AS `security_group_name`,
+        `security_group`.`description` AS `security_group_description`,
+        `nics`.`id` AS `nic_id`,
+        `nics`.`uuid` AS `nic_uuid`,
+        `nics`.`network_id` AS `network_id`,
+        `nics`.`ip4_address` AS `ip_address`,
+        `nics`.`ip6_address` AS `ip6_address`,
+        `nics`.`ip6_gateway` AS `ip6_gateway`,
+        `nics`.`ip6_cidr` AS `ip6_cidr`,
+        `nics`.`default_nic` AS `is_default_nic`,
+        `nics`.`gateway` AS `gateway`,
+        `nics`.`netmask` AS `netmask`,
+        `nics`.`mac_address` AS `mac_address`,
+        `nics`.`broadcast_uri` AS `broadcast_uri`,
+        `nics`.`isolation_uri` AS `isolation_uri`,
+        `vpc`.`id` AS `vpc_id`,
+        `vpc`.`uuid` AS `vpc_uuid`,
+        `networks`.`uuid` AS `network_uuid`,
+        `networks`.`name` AS `network_name`,
+        `networks`.`traffic_type` AS `traffic_type`,
+        `networks`.`guest_type` AS `guest_type`,
+        `user_ip_address`.`id` AS `public_ip_id`,
+        `user_ip_address`.`uuid` AS `public_ip_uuid`,
+        `user_ip_address`.`public_ip_address` AS `public_ip_address`,
+        `ssh_keypairs`.`keypair_name` AS `keypair_name`,
+        `resource_tags`.`id` AS `tag_id`,
+        `resource_tags`.`uuid` AS `tag_uuid`,
+        `resource_tags`.`key` AS `tag_key`,
+        `resource_tags`.`value` AS `tag_value`,
+        `resource_tags`.`domain_id` AS `tag_domain_id`,
+        `domain`.`uuid` AS `tag_domain_uuid`,
+        `domain`.`name` AS `tag_domain_name`,
+        `resource_tags`.`account_id` AS `tag_account_id`,
+        `account`.`account_name` AS `tag_account_name`,
+        `resource_tags`.`resource_id` AS `tag_resource_id`,
+        `resource_tags`.`resource_uuid` AS `tag_resource_uuid`,
+        `resource_tags`.`resource_type` AS `tag_resource_type`,
+        `resource_tags`.`customer` AS `tag_customer`,
+        `async_job`.`id` AS `job_id`,
+        `async_job`.`uuid` AS `job_uuid`,
+        `async_job`.`job_status` AS `job_status`,
+        `async_job`.`account_id` AS `job_account_id`,
+        `affinity_group`.`id` AS `affinity_group_id`,
+        `affinity_group`.`uuid` AS `affinity_group_uuid`,
+        `affinity_group`.`name` AS `affinity_group_name`,
+        `affinity_group`.`description` AS `affinity_group_description`,
+        `vm_instance`.`dynamically_scalable` AS `dynamically_scalable`
+    FROM
+        (((((((((((((((((((((((((((((((((`user_vm`
+        JOIN `vm_instance` ON (((`vm_instance`.`id` = `user_vm`.`id`)
+            AND ISNULL(`vm_instance`.`removed`))))
+        JOIN `account` ON ((`vm_instance`.`account_id` = `account`.`id`)))
+        JOIN `domain` ON ((`vm_instance`.`domain_id` = `domain`.`id`)))
+        LEFT JOIN `guest_os` ON ((`vm_instance`.`guest_os_id` = `guest_os`.`id`)))
+        LEFT JOIN `host_pod_ref` ON ((`vm_instance`.`pod_id` = `host_pod_ref`.`id`)))
+        LEFT JOIN `projects` ON ((`projects`.`project_account_id` = `account`.`id`)))
+        LEFT JOIN `instance_group_vm_map` ON ((`vm_instance`.`id` = `instance_group_vm_map`.`instance_id`)))
+        LEFT JOIN `instance_group` ON ((`instance_group_vm_map`.`group_id` = `instance_group`.`id`)))
+        LEFT JOIN `data_center` ON ((`vm_instance`.`data_center_id` = `data_center`.`id`)))
+        LEFT JOIN `host` ON ((`vm_instance`.`host_id` = `host`.`id`)))
+        LEFT JOIN `vm_template` ON ((`vm_instance`.`vm_template_id` = `vm_template`.`id`)))
+        LEFT JOIN `vm_template` `iso` ON ((`iso`.`id` = `user_vm`.`iso_id`)))
+        LEFT JOIN `service_offering` ON ((`vm_instance`.`service_offering_id` = `service_offering`.`id`)))
+        LEFT JOIN `disk_offering` `svc_disk_offering` ON ((`vm_instance`.`service_offering_id` = `svc_disk_offering`.`id`)))
+        LEFT JOIN `disk_offering` ON ((`vm_instance`.`disk_offering_id` = `disk_offering`.`id`)))
+        LEFT JOIN `backup_offering` ON ((`vm_instance`.`backup_offering_id` = `backup_offering`.`id`)))
+        LEFT JOIN `volumes` ON ((`vm_instance`.`id` = `volumes`.`instance_id`)))
+        LEFT JOIN `storage_pool` ON ((`volumes`.`pool_id` = `storage_pool`.`id`)))
+        LEFT JOIN `security_group_vm_map` ON ((`vm_instance`.`id` = `security_group_vm_map`.`instance_id`)))
+        LEFT JOIN `security_group` ON ((`security_group_vm_map`.`security_group_id` = `security_group`.`id`)))
+        LEFT JOIN `nics` ON (((`vm_instance`.`id` = `nics`.`instance_id`)
+            AND ISNULL(`nics`.`removed`))))
+        LEFT JOIN `networks` ON ((`nics`.`network_id` = `networks`.`id`)))
+        LEFT JOIN `vpc` ON (((`networks`.`vpc_id` = `vpc`.`id`)
+            AND ISNULL(`vpc`.`removed`))))
+        LEFT JOIN `user_ip_address` ON ((`user_ip_address`.`vm_id` = `vm_instance`.`id`)))
+        LEFT JOIN `user_vm_details` `ssh_details` ON (((`ssh_details`.`vm_id` = `vm_instance`.`id`)
+            AND (`ssh_details`.`name` = 'SSH.PublicKey'))))
+        LEFT JOIN `ssh_keypairs` ON (((`ssh_keypairs`.`public_key` = `ssh_details`.`value`)
+            AND (`ssh_keypairs`.`account_id` = `account`.`id`))))
+        LEFT JOIN `resource_tags` ON (((`resource_tags`.`resource_id` = `vm_instance`.`id`)
+            AND (`resource_tags`.`resource_type` = 'UserVm'))))
+        LEFT JOIN `async_job` ON (((`async_job`.`instance_id` = `vm_instance`.`id`)
+            AND (`async_job`.`instance_type` = 'VirtualMachine')
+            AND (`async_job`.`job_status` = 0))))
+        LEFT JOIN `affinity_group_vm_map` ON ((`vm_instance`.`id` = `affinity_group_vm_map`.`instance_id`)))
+        LEFT JOIN `affinity_group` ON ((`affinity_group_vm_map`.`affinity_group_id` = `affinity_group`.`id`)))
+        LEFT JOIN `user_vm_details` `custom_cpu` ON (((`custom_cpu`.`vm_id` = `vm_instance`.`id`)
+            AND (`custom_cpu`.`name` = 'CpuNumber'))))
+        LEFT JOIN `user_vm_details` `custom_speed` ON (((`custom_speed`.`vm_id` = `vm_instance`.`id`)
+            AND (`custom_speed`.`name` = 'CpuSpeed'))))
+        LEFT JOIN `user_vm_details` `custom_ram_size` ON (((`custom_ram_size`.`vm_id` = `vm_instance`.`id`)
+            AND (`custom_ram_size`.`name` = 'memory'))));
+
 -- Fix OS category for some Ubuntu and RedHat OS-es
 UPDATE `cloud`.`guest_os` SET `category_id`='10' WHERE `id`=277 AND display_name="Ubuntu 17.04";
 UPDATE `cloud`.`guest_os` SET `category_id`='10' WHERE `id`=278 AND display_name="Ubuntu 17.10";
diff --git a/engine/schema/src/test/java/com/cloud/storage/dao/DiskOfferingDaoImplTest.java b/engine/schema/src/test/java/com/cloud/storage/dao/DiskOfferingDaoImplTest.java
new file mode 100644
index 0000000..3dc36d6
--- /dev/null
+++ b/engine/schema/src/test/java/com/cloud/storage/dao/DiskOfferingDaoImplTest.java
@@ -0,0 +1,56 @@
+// 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 com.cloud.storage.dao;
+
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class DiskOfferingDaoImplTest {
+
+    private final DiskOfferingDaoImpl dao = new DiskOfferingDaoImpl();
+
+    @Test(expected = CloudRuntimeException.class)
+    public void testGetClosestDiskSizeInGBNegativeSize() {
+        long size = -4 * DiskOfferingDaoImpl.GB_UNIT_BYTES;
+        dao.getClosestDiskSizeInGB(size);
+    }
+
+    @Test
+    public void testGetClosestDiskSizeInGBSizeGB() {
+        int gbUnits = 5;
+        long size = gbUnits * DiskOfferingDaoImpl.GB_UNIT_BYTES;
+        long sizeInGB = dao.getClosestDiskSizeInGB(size);
+        Assert.assertEquals(gbUnits, sizeInGB);
+    }
+
+    @Test
+    public void testGetClosestDiskSizeInGBSizeGBRest() {
+        int gbUnits = 5;
+        long size = gbUnits * DiskOfferingDaoImpl.GB_UNIT_BYTES + 12345;
+        long sizeInGB = dao.getClosestDiskSizeInGB(size);
+        Assert.assertEquals(gbUnits + 1, sizeInGB);
+    }
+
+    @Test
+    public void testGetClosestDiskSizeInGBSizeLessOneGB() {
+        int gbUnits = 1;
+        long size = gbUnits * DiskOfferingDaoImpl.GB_UNIT_BYTES - 12345;
+        long sizeInGB = dao.getClosestDiskSizeInGB(size);
+        Assert.assertEquals(gbUnits, sizeInGB);
+    }
+}
diff --git a/framework/db/src/main/java/com/cloud/utils/db/GenericDao.java b/framework/db/src/main/java/com/cloud/utils/db/GenericDao.java
index 04c0882..38f9d9f 100644
--- a/framework/db/src/main/java/com/cloud/utils/db/GenericDao.java
+++ b/framework/db/src/main/java/com/cloud/utils/db/GenericDao.java
@@ -234,6 +234,8 @@ public interface GenericDao<T, ID extends Serializable> {
      */
     void expunge();
 
+    boolean unremove(ID id);
+
     public <K> K getNextInSequence(Class<K> clazz, String name);
 
     /**
diff --git a/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java b/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java
index 314cd7c..f34b8ed 100644
--- a/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java
+++ b/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java
@@ -1785,6 +1785,34 @@ public abstract class GenericDaoBase<T, ID extends Serializable> extends Compone
         }
     }
 
+    @Override
+    public boolean unremove(ID id) {
+        if (_removed == null) {
+            return false;
+        }
+
+        final TransactionLegacy txn = TransactionLegacy.currentTxn();
+        PreparedStatement pstmt = null;
+        try {
+            txn.start();
+            pstmt = txn.prepareAutoCloseStatement(_removeSql.first());
+            final Attribute[] attrs = _removeSql.second();
+            pstmt.setObject(1, null);
+            for (int i = 0; i < attrs.length - 1; i++) {
+                prepareAttribute(i + 2, pstmt, attrs[i], id);
+            }
+
+            final int result = pstmt.executeUpdate();
+            txn.commit();
+            if (_cache != null) {
+                _cache.remove(id);
+            }
+            return result > 0;
+        } catch (final SQLException e) {
+            throw new CloudRuntimeException("DB Exception on: " + pstmt, e);
+        }
+    }
+
     @DB()
     protected void setField(final Object entity, final ResultSet rs, ResultSetMetaData meta, final int index) throws SQLException {
         Attribute attr = _allColumns.get(new Pair<String, String>(meta.getTableName(index), meta.getColumnName(index)));
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java
index 769f9ae..3eb949f 100644
--- a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java
@@ -158,6 +158,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
             case QuotaTypes.ISO:
             case QuotaTypes.VOLUME:
             case QuotaTypes.VM_SNAPSHOT:
+            case QuotaTypes.BACKUP:
                 qu = updateQuotaDiskUsage(usageRecord, aggregationRatio, usageRecord.getUsageType());
                 if (qu != null) {
                     quotaListForAccount.add(qu);
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java
index 13788f7..babb4ed 100644
--- a/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaTypes.java
@@ -16,12 +16,12 @@
 // under the License.
 package org.apache.cloudstack.quota.constant;
 
-import org.apache.cloudstack.usage.UsageTypes;
-
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.cloudstack.usage.UsageTypes;
+
 public class QuotaTypes extends UsageTypes {
     public static final int CPU_CLOCK_RATE = 15;
     public static final int CPU_NUMBER = 16;
@@ -57,6 +57,7 @@ public class QuotaTypes extends UsageTypes {
         quotaTypeList.put(VM_SNAPSHOT, new QuotaTypes(VM_SNAPSHOT, "VM_SNAPSHOT", "GB-Month", "VM Snapshot storage usage"));
         quotaTypeList.put(VOLUME_SECONDARY, new QuotaTypes(VOLUME_SECONDARY, "VOLUME_SECONDARY", "GB-Month", "Volume secondary storage usage"));
         quotaTypeList.put(VM_SNAPSHOT_ON_PRIMARY, new QuotaTypes(VM_SNAPSHOT_ON_PRIMARY, "VM_SNAPSHOT_ON_PRIMARY", "GB-Month", "VM Snapshot primary storage usage"));
+        quotaTypeList.put(BACKUP, new QuotaTypes(BACKUP, "BACKUP", "GB-Month", "Backup storage usage"));
         quotaTypeList.put(CPU_CLOCK_RATE, new QuotaTypes(CPU_CLOCK_RATE, "CPU_CLOCK_RATE", "Compute-Month", "Quota tariff for using 1 CPU of clock rate 100MHz"));
         quotaTypeList.put(CPU_NUMBER, new QuotaTypes(CPU_NUMBER, "CPU_NUMBER", "Compute-Month", "Quota tariff for running VM that has 1vCPU"));
         quotaTypeList.put(MEMORY, new QuotaTypes(MEMORY, "MEMORY", "Compute-Month", "Quota tariff for using 1MB of RAM"));
diff --git a/plugins/backup/dummy/pom.xml b/plugins/backup/dummy/pom.xml
new file mode 100644
index 0000000..5d6d7bb
--- /dev/null
+++ b/plugins/backup/dummy/pom.xml
@@ -0,0 +1,41 @@
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>cloud-plugin-backup-dummy</artifactId>
+    <name>Apache CloudStack Plugin - Dummy Backup and Recovery Plugin</name>
+    <parent>
+        <artifactId>cloudstack-plugins</artifactId>
+        <groupId>org.apache.cloudstack</groupId>
+        <version>4.14.0.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cloudstack</groupId>
+            <artifactId>cloud-utils</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java b/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java
new file mode 100644
index 0000000..449dfbf
--- /dev/null
+++ b/plugins/backup/dummy/src/main/java/org/apache/cloudstack/backup/DummyBackupProvider.java
@@ -0,0 +1,134 @@
+// 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;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.backup.dao.BackupDao;
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.Pair;
+import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+
+public class DummyBackupProvider extends AdapterBase implements BackupProvider {
+
+    private static final Logger s_logger = Logger.getLogger(DummyBackupProvider.class);
+
+    @Inject
+    private BackupDao backupDao;
+
+    @Override
+    public String getName() {
+        return "dummy";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Dummy Backup Plugin";
+    }
+
+    @Override
+    public List<BackupOffering> listBackupOfferings(Long zoneId) {
+        s_logger.debug("Listing backup policies on Dummy B&R Plugin");
+        BackupOffering policy1 = new BackupOfferingVO(1, "gold-policy", "dummy", "Golden Policy", "Gold description", true);
+        BackupOffering policy2 = new BackupOfferingVO(1, "silver-policy", "dummy", "Silver Policy", "Silver description", true);
+        return Arrays.asList(policy1, policy2);
+    }
+
+    @Override
+    public boolean isValidProviderOffering(Long zoneId, String uuid) {
+        s_logger.debug("Checking if backup offering exists on the Dummy Backup Provider");
+        return true;
+    }
+
+    @Override
+    public boolean assignVMToBackupOffering(VirtualMachine vm, BackupOffering backupOffering) {
+        s_logger.debug("Creating VM backup for VM " + vm.getInstanceName() + " from backup offering " + backupOffering.getName());
+        ((VMInstanceVO) vm).setBackupExternalId("dummy-external-backup-id");
+        return true;
+    }
+
+    @Override
+    public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) {
+        s_logger.debug("Restoring vm " + vm.getUuid() + "from backup " + backup.getUuid() + " on the Dummy Backup Provider");
+        return true;
+    }
+
+    @Override
+    public Pair<Boolean, String> restoreBackedUpVolume(Backup backup, String volumeUuid, String hostIp, String dataStoreUuid) {
+        s_logger.debug("Restoring volume " + volumeUuid + "from backup " + backup.getUuid() + " on the Dummy Backup Provider");
+        throw new CloudRuntimeException("Dummy plugin does not support this feature");
+    }
+
+    @Override
+    public Map<VirtualMachine, Backup.Metric> getBackupMetrics(Long zoneId, List<VirtualMachine> vms) {
+        final Map<VirtualMachine, Backup.Metric> metrics = new HashMap<>();
+        final Backup.Metric metric = new Backup.Metric(1000L, 100L);
+        for (VirtualMachine vm : vms) {
+            metrics.put(vm, metric);
+        }
+        return metrics;
+    }
+
+    @Override
+    public boolean removeVMFromBackupOffering(VirtualMachine vm) {
+        s_logger.debug("Removing VM ID " + vm.getUuid() + " from backup offering by the Dummy Backup Provider");
+        return true;
+    }
+
+    @Override
+    public boolean willDeleteBackupsOnOfferingRemoval() {
+        return true;
+    }
+
+    @Override
+    public boolean takeBackup(VirtualMachine vm) {
+        s_logger.debug("Starting backup for VM ID " + vm.getUuid() + " on Dummy provider");
+
+        BackupVO backup = new BackupVO();
+        backup.setVmId(vm.getId());
+        backup.setExternalId("dummy-external-id");
+        backup.setType("FULL");
+        backup.setDate(new Date().toString());
+        backup.setSize(1024L);
+        backup.setProtectedSize(1024000L);
+        backup.setStatus(Backup.Status.BackedUp);
+        backup.setBackupOfferingId(vm.getBackupOfferingId());
+        backup.setAccountId(vm.getAccountId());
+        backup.setDomainId(vm.getDomainId());
+        backup.setZoneId(vm.getDataCenterId());
+        return backupDao.persist(backup) != null;
+    }
+
+    @Override
+    public boolean deleteBackup(Backup backup) {
+        return true;
+    }
+
+    @Override
+    public void syncBackups(VirtualMachine vm, Backup.Metric metric) {
+    }
+}
diff --git a/plugins/backup/dummy/src/main/resources/META-INF/cloudstack/dummy-backup/module.properties b/plugins/backup/dummy/src/main/resources/META-INF/cloudstack/dummy-backup/module.properties
new file mode 100644
index 0000000..5969fb2
--- /dev/null
+++ b/plugins/backup/dummy/src/main/resources/META-INF/cloudstack/dummy-backup/module.properties
@@ -0,0 +1,18 @@
+# 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.
+name=dummy-backup
+parent=backup
diff --git a/plugins/backup/dummy/src/main/resources/META-INF/cloudstack/dummy-backup/spring-backup-dummy-context.xml b/plugins/backup/dummy/src/main/resources/META-INF/cloudstack/dummy-backup/spring-backup-dummy-context.xml
new file mode 100644
index 0000000..e154f9f
--- /dev/null
+++ b/plugins/backup/dummy/src/main/resources/META-INF/cloudstack/dummy-backup/spring-backup-dummy-context.xml
@@ -0,0 +1,27 @@
+<!--
+  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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
+>
+    <bean id="dummyBackupRecoveryDriver" class="org.apache.cloudstack.backup.DummyBackupProvider">
+        <property name="name" value="dummy" />
+    </bean>
+</beans>
diff --git a/plugins/backup/veeam/pom.xml b/plugins/backup/veeam/pom.xml
new file mode 100644
index 0000000..e82bf1b
--- /dev/null
+++ b/plugins/backup/veeam/pom.xml
@@ -0,0 +1,54 @@
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>cloud-plugin-backup-veeam</artifactId>
+  <name>Apache CloudStack Plugin - Veeam Backup and Recovery Plugin</name>
+  <parent>
+    <artifactId>cloudstack-plugins</artifactId>
+    <groupId>org.apache.cloudstack</groupId>
+    <version>4.14.0.0-SNAPSHOT</version>
+    <relativePath>../../pom.xml</relativePath>
+  </parent>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.cloudstack</groupId>
+      <artifactId>cloud-plugin-hypervisor-vmware</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.dataformat</groupId>
+      <artifactId>jackson-dataformat-xml</artifactId>
+      <version>${cs.jackson.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>${cs.commons-lang3.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.github.tomakehurst</groupId>
+      <artifactId>wiremock-standalone</artifactId>
+      <version>${cs.wiremock.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
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
new file mode 100644
index 0000000..ee12792
--- /dev/null
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/VeeamBackupProvider.java
@@ -0,0 +1,310 @@
+// 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;
+
+import java.net.URISyntaxException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.api.InternalIdentity;
+import org.apache.cloudstack.backup.dao.BackupDao;
+import org.apache.cloudstack.backup.veeam.VeeamClient;
+import org.apache.cloudstack.backup.veeam.api.Job;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.log4j.Logger;
+
+import com.cloud.hypervisor.Hypervisor;
+import com.cloud.hypervisor.vmware.VmwareDatacenter;
+import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMap;
+import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao;
+import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao;
+import com.cloud.utils.Pair;
+import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallbackNoReturn;
+import com.cloud.utils.db.TransactionStatus;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+
+public class VeeamBackupProvider extends AdapterBase implements BackupProvider, Configurable {
+
+    private static final Logger LOG = Logger.getLogger(VeeamBackupProvider.class);
+    public static final String BACKUP_IDENTIFIER = "-CSBKP-";
+
+    public ConfigKey<String> VeeamUrl = new ConfigKey<>("Advanced", String.class,
+            "backup.plugin.veeam.url", "https://localhost:9398/api/",
+            "The Veeam backup and recovery URL.", true, ConfigKey.Scope.Zone);
+
+    private ConfigKey<String> VeeamUsername = new ConfigKey<>("Advanced", String.class,
+            "backup.plugin.veeam.username", "administrator",
+            "The Veeam backup and recovery username.", true, ConfigKey.Scope.Zone);
+
+    private ConfigKey<String> VeeamPassword = new ConfigKey<>("Secure", String.class,
+            "backup.plugin.veeam.password", "",
+            "The Veeam backup and recovery password.", true, ConfigKey.Scope.Zone);
+
+    private ConfigKey<Boolean> VeeamValidateSSLSecurity = new ConfigKey<>("Advanced", Boolean.class, "backup.plugin.veeam.validate.ssl", "false",
+            "When set to true, this will validate the SSL certificate when connecting to https/ssl enabled Veeam API service.", true, ConfigKey.Scope.Zone);
+
+    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);
+
+    @Inject
+    private VmwareDatacenterZoneMapDao vmwareDatacenterZoneMapDao;
+    @Inject
+    private VmwareDatacenterDao vmwareDatacenterDao;
+    @Inject
+    private BackupDao backupDao;
+
+    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));
+        } catch (URISyntaxException e) {
+            throw new CloudRuntimeException("Failed to parse Veeam API URL: " + e.getMessage());
+        } catch (NoSuchAlgorithmException | KeyManagementException e) {
+            LOG.error("Failed to build Veeam API client due to: ", e);
+        }
+        throw new CloudRuntimeException("Failed to build Veeam API client");
+    }
+
+    public List<BackupOffering> listBackupOfferings(final Long zoneId) {
+        List<BackupOffering> policies = new ArrayList<>();
+        for (final BackupOffering policy : getClient(zoneId).listJobs()) {
+            if (!policy.getName().contains(BACKUP_IDENTIFIER)) {
+                policies.add(policy);
+            }
+        }
+        return policies;
+    }
+
+    @Override
+    public boolean isValidProviderOffering(final Long zoneId, final String uuid) {
+        List<BackupOffering> policies = listBackupOfferings(zoneId);
+        if (CollectionUtils.isEmpty(policies)) {
+            return false;
+        }
+        for (final BackupOffering policy : policies) {
+            if (policy.getExternalId().equals(uuid)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private VmwareDatacenter findVmwareDatacenterForVM(final VirtualMachine vm) {
+        if (vm == null || vm.getHypervisorType() != Hypervisor.HypervisorType.VMware) {
+            throw new CloudRuntimeException("The Veeam backup provider is only applicable for VMware VMs");
+        }
+        final VmwareDatacenterZoneMap zoneMap = vmwareDatacenterZoneMapDao.findByZoneId(vm.getDataCenterId());
+        if (zoneMap == null) {
+            throw new CloudRuntimeException("Failed to find a mapped VMware datacenter for zone id:" + vm.getDataCenterId());
+        }
+        final VmwareDatacenter vmwareDatacenter = vmwareDatacenterDao.findById(zoneMap.getVmwareDcId());
+        if (vmwareDatacenter == null) {
+            throw new CloudRuntimeException("Failed to find a valid VMware datacenter mapped for zone id:" + vm.getDataCenterId());
+        }
+        return vmwareDatacenter;
+    }
+
+    private String getGuestBackupName(final String instanceName, final String uuid) {
+        return String.format("%s%s%s", instanceName, BACKUP_IDENTIFIER, uuid);
+    }
+
+    @Override
+    public boolean assignVMToBackupOffering(final VirtualMachine vm, final BackupOffering backupOffering) {
+        final VeeamClient client = getClient(vm.getDataCenterId());
+        final Job parentJob = client.listJob(backupOffering.getExternalId());
+        final String clonedJobName = getGuestBackupName(vm.getInstanceName(), vm.getUuid());
+
+        if (!client.cloneVeeamJob(parentJob, clonedJobName)) {
+            LOG.error("Failed to clone pre-defined Veeam job (backup offering) for backup offering ID: " + backupOffering.getExternalId() + " but will check the list of jobs again if it was eventually succeeded.");
+        }
+
+        for (final BackupOffering job : client.listJobs()) {
+            if (job.getName().equals(clonedJobName)) {
+                final Job clonedJob = client.listJob(job.getExternalId());
+                if (clonedJob.getScheduleConfigured() && !clonedJob.getScheduleEnabled()) {
+                    client.toggleJobSchedule(clonedJob.getId());
+                }
+                LOG.debug("Veeam job (backup offering) for backup offering ID: " + backupOffering.getExternalId() + " found, now trying to assign the VM to the job.");
+                final VmwareDatacenter vmwareDC = findVmwareDatacenterForVM(vm);
+                if (client.addVMToVeeamJob(job.getExternalId(), vm.getInstanceName(), vmwareDC.getVcenterHost())) {
+                    ((VMInstanceVO) vm).setBackupExternalId(job.getExternalId());
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean removeVMFromBackupOffering(final VirtualMachine vm) {
+        final VeeamClient client = getClient(vm.getDataCenterId());
+        final VmwareDatacenter vmwareDC = findVmwareDatacenterForVM(vm);
+        try {
+            if (!client.removeVMFromVeeamJob(vm.getBackupExternalId(), vm.getInstanceName(), vmwareDC.getVcenterHost())) {
+                LOG.warn("Failed to remove VM from Veeam Job id: " + vm.getBackupExternalId());
+            }
+        } catch (Exception e) {
+            LOG.debug("VM was removed from the job so could not remove again, trying to delete the veeam job now.", e);
+        }
+
+        final String clonedJobName = getGuestBackupName(vm.getInstanceName(), vm.getUuid());
+        if (!client.deleteJobAndBackup(clonedJobName)) {
+            LOG.warn("Failed to remove Veeam job and backup for job: " + clonedJobName);
+            throw new CloudRuntimeException("Failed to delete Veeam B&R job and backup, an operation may be in progress. Please try again after some time.");
+        }
+        return true;
+    }
+
+    @Override
+    public boolean willDeleteBackupsOnOfferingRemoval() {
+        return true;
+    }
+
+    @Override
+    public boolean takeBackup(final VirtualMachine vm) {
+        final VeeamClient client = getClient(vm.getDataCenterId());
+        return client.startBackupJob(vm.getBackupExternalId());
+    }
+
+    @Override
+    public boolean deleteBackup(Backup backup) {
+        // Veeam does not support removal of a restore point or point-in-time backup
+        throw new CloudRuntimeException("Veeam B&R plugin does not allow removal of backup restore point, to delete the backup chain remove VM from the backup offering");
+    }
+
+    @Override
+    public boolean restoreVMFromBackup(VirtualMachine vm, Backup backup) {
+        final String restorePointId = backup.getExternalId();
+        return getClient(vm.getDataCenterId()).restoreFullVM(vm.getInstanceName(), restorePointId);
+    }
+
+    @Override
+    public Pair<Boolean, String> restoreBackedUpVolume(Backup backup, String volumeUuid, String hostIp, String dataStoreUuid) {
+        final Long zoneId = backup.getZoneId();
+        final String restorePointId = backup.getExternalId();
+        return getClient(zoneId).restoreVMToDifferentLocation(restorePointId, hostIp, dataStoreUuid);
+    }
+
+    @Override
+    public Map<VirtualMachine, Backup.Metric> getBackupMetrics(final Long zoneId, final List<VirtualMachine> vms) {
+        final Map<VirtualMachine, Backup.Metric> metrics = new HashMap<>();
+        final Map<String, Backup.Metric> backendMetrics = getClient(zoneId).getBackupMetrics();
+        for (final VirtualMachine vm : vms) {
+            if (!backendMetrics.containsKey(vm.getUuid())) {
+                continue;
+            }
+            metrics.put(vm, backendMetrics.get(vm.getUuid()));
+        }
+        return metrics;
+    }
+
+    private List<Backup.RestorePoint> listRestorePoints(VirtualMachine vm) {
+        String backupName = getGuestBackupName(vm.getInstanceName(), vm.getUuid());
+        return getClient(vm.getDataCenterId()).listRestorePoints(backupName, vm.getInstanceName());
+    }
+
+    @Override
+    public void syncBackups(VirtualMachine vm, Backup.Metric metric) {
+        List<Backup.RestorePoint> restorePoints = listRestorePoints(vm);
+        if (restorePoints == null || restorePoints.isEmpty()) {
+            return;
+        }
+        Transaction.execute(new TransactionCallbackNoReturn() {
+            @Override
+            public void doInTransactionWithoutResult(TransactionStatus status) {
+                final List<Backup> backupsInDb = backupDao.listByVmId(null, vm.getId());
+                final List<Long> removeList = backupsInDb.stream().map(InternalIdentity::getId).collect(Collectors.toList());
+                for (final Backup.RestorePoint restorePoint : restorePoints) {
+                    boolean backupExists = false;
+                    for (final Backup backup : backupsInDb) {
+                        if (restorePoint.getId().equals(backup.getExternalId())) {
+                            backupExists = true;
+                            removeList.remove(backup.getId());
+                            if (metric != null) {
+                                ((BackupVO) backup).setSize(metric.getBackupSize());
+                                ((BackupVO) backup).setProtectedSize(metric.getDataSize());
+                                backupDao.update(backup.getId(), ((BackupVO) backup));
+                            }
+                            break;
+                        }
+                    }
+                    if (backupExists) {
+                        continue;
+                    }
+                    BackupVO backup = new BackupVO();
+                    backup.setVmId(vm.getId());
+                    backup.setExternalId(restorePoint.getId());
+                    backup.setType(restorePoint.getType());
+                    backup.setDate(restorePoint.getCreated());
+                    backup.setStatus(Backup.Status.BackedUp);
+                    if (metric != null) {
+                        backup.setSize(metric.getBackupSize());
+                        backup.setProtectedSize(metric.getDataSize());
+                    }
+                    backup.setBackupOfferingId(vm.getBackupOfferingId());
+                    backup.setAccountId(vm.getAccountId());
+                    backup.setDomainId(vm.getDomainId());
+                    backup.setZoneId(vm.getDataCenterId());
+                    backupDao.persist(backup);
+                }
+                for (final Long backupIdToRemove : removeList) {
+                    backupDao.remove(backupIdToRemove);
+                }
+            }
+        });
+    }
+
+    @Override
+    public String getConfigComponentName() {
+        return BackupService.class.getSimpleName();
+    }
+
+    @Override
+    public ConfigKey<?>[] getConfigKeys() {
+        return new ConfigKey[]{
+                VeeamUrl,
+                VeeamUsername,
+                VeeamPassword,
+                VeeamValidateSSLSecurity,
+                VeeamApiRequestTimeout
+        };
+    }
+
+    @Override
+    public String getName() {
+        return "veeam";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Veeam Backup Plugin";
+    }
+}
diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamBackupOffering.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamBackupOffering.java
new file mode 100644
index 0000000..9b1e7b0
--- /dev/null
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamBackupOffering.java
@@ -0,0 +1,78 @@
+// 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;
+
+import java.util.Date;
+
+import org.apache.cloudstack.backup.BackupOffering;
+
+public class VeeamBackupOffering implements BackupOffering {
+
+    private String name;
+    private String uid;
+
+    public VeeamBackupOffering(String name, String uid) {
+        this.name = name;
+        this.uid = uid;
+    }
+
+    @Override
+    public String getExternalId() {
+        return uid;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getDescription() {
+        return "Veeam Backup Offering (Job)";
+    }
+
+    @Override
+    public long getZoneId() {
+        return -1;
+    }
+
+    @Override
+    public boolean isUserDrivenBackupAllowed() {
+        return false;
+    }
+
+    @Override
+    public String getProvider() {
+        return "veeam";
+    }
+
+    @Override
+    public Date getCreated() {
+        return null;
+    }
+
+    @Override
+    public String getUuid() {
+        return uid;
+    }
+
+    @Override
+    public long getId() {
+        return -1;
+    }
+}
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
new file mode 100644
index 0000000..1c0cc72
--- /dev/null
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamClient.java
@@ -0,0 +1,654 @@
+// 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;
+
+import static org.apache.cloudstack.backup.VeeamBackupProvider.BACKUP_IDENTIFIER;
+
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringJoiner;
+import java.util.UUID;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.backup.veeam.api.BackupJobCloneInfo;
+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.Job;
+import org.apache.cloudstack.backup.veeam.api.JobCloneSpec;
+import org.apache.cloudstack.backup.veeam.api.Link;
+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.RestoreSession;
+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.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+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.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.client.HttpClientBuilder;
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.Pair;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.nio.TrustAllManager;
+import com.cloud.utils.ssh.SshHelper;
+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;
+import com.google.common.base.Strings;
+
+public class VeeamClient {
+    private static final Logger LOG = Logger.getLogger(VeeamClient.class);
+
+    private final URI apiURI;
+
+    private final HttpClient httpClient;
+    private static final String RESTORE_VM_SUFFIX = "CS-RSTR-";
+    private static final String SESSION_HEADER = "X-RestSvcSessionId";
+
+    private String veeamServerIp;
+    private String veeamServerUsername;
+    private String veeamServerPassword;
+    private String veeamSessionId = null;
+    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 {
+        this.apiURI = new URI(url);
+
+        final RequestConfig config = RequestConfig.custom()
+                .setConnectTimeout(timeout * 1000)
+                .setConnectionRequestTimeout(timeout * 1000)
+                .setSocketTimeout(timeout * 1000)
+                .build();
+
+        if (!validateCertificate) {
+            final SSLContext sslcontext = SSLUtils.getSSLContext();
+            sslcontext.init(null, new X509TrustManager[]{new TrustAllManager()}, new SecureRandom());
+            final SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, NoopHostnameVerifier.INSTANCE);
+            this.httpClient = HttpClientBuilder.create()
+                    .setDefaultRequestConfig(config)
+                    .setSSLSocketFactory(factory)
+                    .build();
+        } else {
+            this.httpClient = HttpClientBuilder.create()
+                    .setDefaultRequestConfig(config)
+                    .build();
+        }
+
+        authenticate(username, password);
+        setVeeamSshCredentials(this.apiURI.getHost(), username, password);
+    }
+
+    protected void setVeeamSshCredentials(String hostIp, String username, String password) {
+        this.veeamServerIp = hostIp;
+        this.veeamServerUsername = username;
+        this.veeamServerPassword = password;
+    }
+
+    private void authenticate(final String username, final String password) {
+        // https://helpcenter.veeam.com/docs/backup/rest/http_authentication.html?ver=95u4
+        final HttpPost request = new HttpPost(apiURI.toString() + "/sessionMngr/?v=v1_4");
+        request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes()));
+        try {
+            final HttpResponse response = httpClient.execute(request);
+            checkAuthFailure(response);
+            veeamSessionId = response.getFirstHeader(SESSION_HEADER).getValue();
+            if (Strings.isNullOrEmpty(veeamSessionId)) {
+                throw new CloudRuntimeException("Veeam Session ID is not available to perform API requests");
+            }
+            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_CREATED) {
+                throw new CloudRuntimeException("Failed to create and authenticate Veeam API client, please check the settings.");
+            }
+        } catch (final IOException e) {
+            throw new CloudRuntimeException("Failed to authenticate Veeam API service due to:" + e.getMessage());
+        }
+    }
+
+    private void checkAuthFailure(final HttpResponse response) {
+        if (response != null && response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
+            throw new ServerApiException(ApiErrorCode.UNAUTHORIZED, "Veeam B&R API call unauthorized, please ask your administrator to fix integration issues.");
+        }
+    }
+
+    private void checkResponseOK(final HttpResponse response) {
+        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NO_CONTENT) {
+            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) {
+            LOG.debug("HTTP request failed, status code is " + response.getStatusLine().getStatusCode() + ", response is: " + response.toString());
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Got invalid API status code returned by the Veeam server");
+        }
+    }
+
+    private void checkResponseTimeOut(final Exception e) {
+        if (e instanceof ConnectTimeoutException || e instanceof SocketTimeoutException) {
+            throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, "Veeam API operation timed out, please try again.");
+        }
+    }
+
+    private HttpResponse get(final String path) throws IOException {
+        final HttpGet request = new HttpGet(apiURI.toString() + path);
+        request.setHeader(SESSION_HEADER, veeamSessionId);
+        final HttpResponse response = httpClient.execute(request);
+        checkAuthFailure(response);
+        return response;
+    }
+
+    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(SESSION_HEADER, veeamSessionId);
+        request.setHeader("Content-type", "application/xml");
+        if (StringUtils.isNotBlank(xml)) {
+            request.setEntity(new StringEntity(xml));
+        }
+
+        final HttpResponse response = httpClient.execute(request);
+        checkAuthFailure(response);
+        return response;
+    }
+
+    private HttpResponse delete(final String path) throws IOException {
+        final HttpDelete request = new HttpDelete(apiURI.toString() + path);
+        request.setHeader(SESSION_HEADER, veeamSessionId);
+        final HttpResponse response = httpClient.execute(request);
+        checkAuthFailure(response);
+        return response;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //////////////// 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 RestoreSession parseRestoreSessionResponse(HttpResponse response) throws IOException {
+        checkResponseOK(response);
+        final ObjectMapper objectMapper = new XmlMapper();
+        return objectMapper.readValue(response.getEntity().getContent(), RestoreSession.class);
+    }
+
+    private boolean checkTaskStatus(final HttpResponse response) throws IOException {
+        final Task task = parseTaskResponse(response);
+        for (int i = 0; i < 120; 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")) {
+                    Pair<String, String> pair = getRelatedLinkPair(polledTask.getLink());
+                    if (pair != null) {
+                        String url = pair.first();
+                        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 true;
+                }
+                throw new CloudRuntimeException("Failed to assign VM to backup offering 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;
+    }
+
+    private Pair<String, String> getRelatedLinkPair(List<Link> links) {
+        for (Link link : links) {
+            if (link.getRel().equals("Related")) {
+                return new Pair<>(link.getHref(), link.getType());
+            }
+        }
+        return null;
+    }
+
+    ////////////////////////////////////////////////////////
+    //////////////// Public Veeam APIs /////////////////////
+    ////////////////////////////////////////////////////////
+
+    public Ref listBackupRepository(final String backupServerId) {
+        LOG.debug("Trying to list backup repository for backup server id: " + backupServerId);
+        try {
+            final HttpResponse response = get(String.format("/backupServers/%s/repositories", backupServerId));
+            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.getType().equals("RepositoryReference")) {
+                    return ref;
+                }
+            }
+        } catch (final IOException e) {
+            LOG.error("Failed to list Veeam jobs due to:", e);
+            checkResponseTimeOut(e);
+        }
+        return null;
+    }
+
+    public void listAllBackups() {
+        LOG.debug("Trying to list Veeam backups");
+        try {
+            final HttpResponse response = get("/backups");
+            checkResponseOK(response);
+            final ObjectMapper objectMapper = new XmlMapper();
+            final EntityReferences entityReferences = objectMapper.readValue(response.getEntity().getContent(), EntityReferences.class);
+            for (final Ref ref : entityReferences.getRefs()) {
+                LOG.debug("Veeam Backup found, name: " + ref.getName() + ", uid: " + ref.getUid() + ", type: " + ref.getType());
+            }
+        } catch (final IOException e) {
+            LOG.error("Failed to list Veeam backups due to:", e);
+            checkResponseTimeOut(e);
+        }
+    }
+
+    public List<BackupOffering> listJobs() {
+        LOG.debug("Trying to list backup policies that are Veeam jobs");
+        try {
+            final HttpResponse response = get("/jobs");
+            checkResponseOK(response);
+            final ObjectMapper objectMapper = new XmlMapper();
+            final EntityReferences entityReferences = objectMapper.readValue(response.getEntity().getContent(), EntityReferences.class);
+            final List<BackupOffering> policies = new ArrayList<>();
+            if (entityReferences == null || entityReferences.getRefs() == null) {
+                return policies;
+            }
+            for (final Ref ref : entityReferences.getRefs()) {
+                policies.add(new VeeamBackupOffering(ref.getName(), ref.getUid()));
+            }
+            return policies;
+        } catch (final IOException e) {
+            LOG.error("Failed to list Veeam jobs due to:", e);
+            checkResponseTimeOut(e);
+        }
+        return new ArrayList<>();
+    }
+
+    public Job listJob(final String jobId) {
+        LOG.debug("Trying to list veeam job id: " + jobId);
+        try {
+            final HttpResponse response = get(String.format("/jobs/%s?format=Entity",
+                    jobId.replace("urn:veeam:Job:", "")));
+            checkResponseOK(response);
+            final ObjectMapper objectMapper = new XmlMapper();
+            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+            return objectMapper.readValue(response.getEntity().getContent(), Job.class);
+        } catch (final IOException e) {
+            LOG.error("Failed to list Veeam jobs due to:", e);
+            checkResponseTimeOut(e);
+        } catch (final ServerApiException e) {
+            LOG.error(e);
+        }
+        return null;
+    }
+
+    public boolean toggleJobSchedule(final String jobId) {
+        LOG.debug("Trying to toggle schedule for Veeam job: " + jobId);
+        try {
+            final HttpResponse response = post(String.format("/jobs/%s?action=toggleScheduleEnabled", jobId), null);
+            return checkTaskStatus(response);
+        } catch (final IOException e) {
+            LOG.error("Failed to toggle Veeam job schedule due to:", e);
+            checkResponseTimeOut(e);
+        }
+        return false;
+    }
+
+    public boolean startBackupJob(final String jobId) {
+        LOG.debug("Trying to start ad-hoc backup for Veeam job: " + jobId);
+        try {
+            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);
+        }
+        return false;
+    }
+
+    public boolean cloneVeeamJob(final Job parentJob, final String clonedJobName) {
+        LOG.debug("Trying to clone veeam job: " + parentJob.getUid() + " with backup uuid: " + clonedJobName);
+        try {
+            final Ref repositoryRef =  listBackupRepository(parentJob.getBackupServerId());
+            final BackupJobCloneInfo cloneInfo = new BackupJobCloneInfo();
+            cloneInfo.setJobName(clonedJobName);
+            cloneInfo.setFolderName(clonedJobName);
+            cloneInfo.setRepositoryUid(repositoryRef.getUid());
+            final JobCloneSpec cloneSpec = new JobCloneSpec(cloneInfo);
+            final HttpResponse response = post(String.format("/jobs/%s?action=clone", parentJob.getId()), cloneSpec);
+            return checkTaskStatus(response);
+        } catch (final Exception e) {
+            LOG.warn("Exception caught while trying to clone Veeam job:", e);
+        }
+        return false;
+    }
+
+    public boolean addVMToVeeamJob(final String jobId, final String vmwareInstanceName, final String vmwareDcName) {
+        LOG.debug("Trying to add VM to backup offering that is 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 offering 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 offering that is a Veeam job: " + jobId);
+        try {
+            final String hierarchyId = findDCHierarchy(vmwareDcName);
+            final String veeamVmRefId = lookupVM(hierarchyId, 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);
+            if (jobObjects == null || jobObjects.getObjects() == null) {
+                LOG.warn("No objects found in the Veeam job " + jobId);
+                return false;
+            }
+            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);
+                }
+            }
+            LOG.warn(vmwareInstanceName + " VM was not found to be attached to Veaam job (backup offering): " + jobId);
+            return false;
+        } catch (final IOException e) {
+            LOG.error("Failed to list Veeam jobs due to:", e);
+            checkResponseTimeOut(e);
+        }
+        return false;
+    }
+
+    public boolean restoreFullVM(final String vmwareInstanceName, final String restorePointId) {
+        LOG.debug("Trying to restore full VM: " + vmwareInstanceName + " from backup");
+        try {
+            final HttpResponse response = post(String.format("/vmRestorePoints/%s?action=restore", restorePointId), null);
+            return checkTaskStatus(response);
+        } catch (final IOException e) {
+            LOG.error("Failed to restore full VM due to: ", e);
+            checkResponseTimeOut(e);
+        }
+        throw new CloudRuntimeException("Failed to restore full VM from backup");
+    }
+
+    /////////////////////////////////////////////////////////////////
+    //////////////// Public Veeam PS based APIs /////////////////////
+    /////////////////////////////////////////////////////////////////
+
+    /**
+     * Generate a single command to be passed through SSH
+     */
+    protected String transformPowerShellCommandList(List<String> cmds) {
+        StringJoiner joiner = new StringJoiner(";");
+        joiner.add("PowerShell Add-PSSnapin VeeamPSSnapin");
+        for (String cmd : cmds) {
+            joiner.add(cmd);
+        }
+        return joiner.toString();
+    }
+
+    /**
+     * Execute a list of commands in a single call on PowerShell through SSH
+     */
+    protected Pair<Boolean, String> executePowerShellCommands(List<String> cmds) {
+        try {
+            Pair<Boolean, String> pairResult = SshHelper.sshExecute(veeamServerIp, veeamServerPort,
+                    veeamServerUsername, null, veeamServerPassword,
+                    transformPowerShellCommandList(cmds),
+                    120000, 120000, 3600000);
+            return pairResult;
+        } catch (Exception e) {
+            throw new CloudRuntimeException("Error while executing PowerShell commands due to: " + e.getMessage());
+        }
+    }
+
+    public boolean setJobSchedule(final String jobName) {
+        Pair<Boolean, String> result = executePowerShellCommands(Arrays.asList(
+                String.format("$job = Get-VBRJob -Name \"%s\"", jobName),
+                "if ($job) { Set-VBRJobSchedule -Job $job -Daily -At \"11:00\" -DailyKind Weekdays }"
+        ));
+        return result.first() && !result.second().isEmpty() && !result.second().contains("Failed to delete");
+    }
+
+    public boolean deleteJobAndBackup(final String jobName) {
+        Pair<Boolean, String> result = executePowerShellCommands(Arrays.asList(
+                String.format("$job = Get-VBRJob -Name \"%s\"", jobName),
+                "if ($job) { Remove-VBRJob -Job $job -Confirm:$false }",
+                String.format("$backup = Get-VBRBackup -Name \"%s\"", jobName),
+                "if ($backup) { Remove-VBRBackup -Backup $backup -FromDisk -Confirm:$false }",
+                "$repo = Get-VBRBackupRepository",
+                "Sync-VBRBackupRepository -Repository $repo"
+        ));
+        return result.first() && !result.second().contains("Failed to delete");
+    }
+
+    public Map<String, Backup.Metric> getBackupMetrics() {
+        final String separator = "=====";
+        final List<String> cmds = Arrays.asList(
+            "$backups = Get-VBRBackup",
+            "foreach ($backup in $backups) {" +
+               "$backup.JobName;" +
+               "$storageGroups = $backup.GetStorageGroups();" +
+               "foreach ($group in $storageGroups) {" +
+                    "$usedSize = 0;" +
+                    "$dataSize = 0;" +
+                    "$sizePerStorage = $group.GetStorages().Stats.BackupSize;" +
+                    "$dataPerStorage = $group.GetStorages().Stats.DataSize;" +
+                    "foreach ($size in $sizePerStorage) {" +
+                        "$usedSize += $size;" +
+                    "}" +
+                    "foreach ($size in $dataPerStorage) {" +
+                        "$dataSize += $size;" +
+                    "}" +
+                    "$usedSize;" +
+                    "$dataSize;" +
+               "}" +
+               "echo \"" + separator + "\"" +
+            "}"
+        );
+        Pair<Boolean, String> response = executePowerShellCommands(cmds);
+        final Map<String, Backup.Metric> sizes = new HashMap<>();
+        for (final String block : response.second().split(separator + "\r\n")) {
+            final String[] parts = block.split("\r\n");
+            if (parts.length != 3) {
+                continue;
+            }
+            final String backupName = parts[0];
+            if (backupName != null && backupName.contains(BACKUP_IDENTIFIER)) {
+                final String[] names = backupName.split(BACKUP_IDENTIFIER);
+                sizes.put(names[names.length - 1], new Backup.Metric(Long.valueOf(parts[1]), Long.valueOf(parts[2])));
+            }
+        }
+        return sizes;
+    }
+
+    private Backup.RestorePoint getRestorePointFromBlock(String[] parts) {
+        String id = null;
+        String created = null;
+        String type = null;
+        for (String part : parts) {
+            if (part.matches("Id(\\s)+:(.)*")) {
+                String[] split = part.split(":");
+                id = split[1].trim();
+            } else if (part.matches("CreationTime(\\s)+:(.)*")) {
+                String [] split = part.split(":", 2);
+                created = split[1].trim();
+            } else if (part.matches("Type(\\s)+:(.)*")) {
+                String [] split = part.split(":");
+                type = split[1].trim();
+            }
+        }
+        return new Backup.RestorePoint(id, created, type);
+    }
+
+    public List<Backup.RestorePoint> listRestorePoints(String backupName, String vmInternalName) {
+        final List<String> cmds = Arrays.asList(
+                String.format("$backup = Get-VBRBackup -Name \"%s\"", backupName),
+                String.format("if ($backup) { (Get-VBRRestorePoint -Backup:$backup -Name \"%s\" ^| Where-Object {$_.IsConsistent -eq $true}) }", vmInternalName)
+        );
+        Pair<Boolean, String> response = executePowerShellCommands(cmds);
+        final List<Backup.RestorePoint> restorePoints = new ArrayList<>();
+        if (response == null || !response.first()) {
+            LOG.debug("Veeam restore point listing failed due to: " + (response != null ? response.second() : "no powershell output returned"));
+            return restorePoints;
+        }
+        for (final String block : response.second().split("\r\n\r\n")) {
+            if (block.isEmpty()) {
+                continue;
+            }
+            final String[] parts = block.split("\r\n");
+            restorePoints.add(getRestorePointFromBlock(parts));
+        }
+        return restorePoints;
+    }
+
+    public Pair<Boolean, String> restoreVMToDifferentLocation(String restorePointId, String hostIp, String dataStoreUuid) {
+        final String restoreLocation = RESTORE_VM_SUFFIX + UUID.randomUUID().toString();
+        final String datastoreId = dataStoreUuid.replace("-","");
+        final List<String> cmds = Arrays.asList(
+                "$points = Get-VBRRestorePoint",
+                String.format("foreach($point in $points) { if ($point.Id -eq '%s') { break; } }", restorePointId),
+                String.format("$server = Get-VBRServer -Name \"%s\"", hostIp),
+                String.format("$ds = Find-VBRViDatastore -Server:$server -Name \"%s\"", datastoreId),
+                String.format("$job = Start-VBRRestoreVM -RestorePoint:$point -Server:$server -Datastore:$ds -VMName \"%s\" -RunAsync", restoreLocation),
+                "while (-not (Get-VBRRestoreSession -Id $job.Id).IsCompleted) { Start-Sleep -Seconds 10 }"
+        );
+        Pair<Boolean, String> result = executePowerShellCommands(cmds);
+        if (result == null || !result.first()) {
+            throw new CloudRuntimeException("Failed to restore VM to location " + restoreLocation);
+        }
+        return new Pair<>(result.first(), restoreLocation);
+    }
+}
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamObject.java
similarity index 66%
copy from engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
copy to plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamObject.java
index 89e2c83..6ecf080 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/VeeamObject.java
@@ -14,21 +14,15 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.storage.dao;
 
-import java.util.List;
-
-import com.cloud.storage.DiskOfferingVO;
-import com.cloud.utils.db.GenericDao;
-
-public interface DiskOfferingDao extends GenericDao<DiskOfferingVO, Long> {
-
-    List<DiskOfferingVO> findPrivateDiskOffering();
+package org.apache.cloudstack.backup.veeam;
 
-    List<DiskOfferingVO> findPublicDiskOfferings();
-
-    DiskOfferingVO findByUniqueName(String uniqueName);
-
-    DiskOfferingVO persistDeafultDiskOffering(DiskOfferingVO offering);
+import java.util.List;
 
+public interface VeeamObject {
+    String getUuid();
+    String getName();
+    String getHref();
+    String getType();
+    List<VeeamObject> getLinks();
 }
diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/BackupJobCloneInfo.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/BackupJobCloneInfo.java
new file mode 100644
index 0000000..fdb6081
--- /dev/null
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/BackupJobCloneInfo.java
@@ -0,0 +1,58 @@
+// 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 = "BackupJobCloneInfo")
+public class BackupJobCloneInfo {
+
+    @JacksonXmlProperty(localName = "JobName")
+    private String jobName;
+
+    @JacksonXmlProperty(localName = "FolderName")
+    private String folderName;
+
+    @JacksonXmlProperty(localName = "RepositoryUid")
+    private String repositoryUid;
+
+    public String getJobName() {
+        return jobName;
+    }
+
+    public void setJobName(String jobName) {
+        this.jobName = jobName;
+    }
+
+    public String getFolderName() {
+        return folderName;
+    }
+
+    public void setFolderName(String folderName) {
+        this.folderName = folderName;
+    }
+
+    public String getRepositoryUid() {
+        return repositoryUid;
+    }
+
+    public void setRepositoryUid(String repositoryUid) {
+        this.repositoryUid = repositoryUid;
+    }
+}
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/CreateObjectInJobSpec.java
similarity index 52%
copy from vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java
copy to plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/CreateObjectInJobSpec.java
index e2797d3..16de447 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/CreateObjectInJobSpec.java
@@ -14,28 +14,33 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.hypervisor.vmware.mo;
 
-import java.util.List;
+package org.apache.cloudstack.backup.veeam.api;
 
-import com.vmware.vim25.ManagedObjectReference;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 
-import com.cloud.hypervisor.vmware.util.VmwareContext;
+@JacksonXmlRootElement(localName = "CreateObjectInJobSpec", namespace = "http://www.veeam.com/ent/v1.0")
+public class CreateObjectInJobSpec {
+    @JacksonXmlProperty(localName = "HierarchyObjRef")
+    String objRef;
 
-public class NetworkMO extends BaseMO {
-    public NetworkMO(VmwareContext context, ManagedObjectReference morCluster) {
-        super(context, morCluster);
+    @JacksonXmlProperty(localName = "HierarchyObjName")
+    String objName;
+
+    public String getObjRef() {
+        return objRef;
     }
 
-    public NetworkMO(VmwareContext context, String morType, String morValue) {
-        super(context, morType, morValue);
+    public void setObjRef(String objRef) {
+        this.objRef = objRef;
     }
 
-    public void destroyNetwork() throws Exception {
-        _context.getService().destroyNetwork(_mor);
+    public String getObjName() {
+        return objName;
     }
 
-    public List<ManagedObjectReference> getVMsOnNetwork() throws Exception {
-        return _context.getVimClient().getDynamicProperty(_mor, "vm");
+    public void setObjName(String objName) {
+        this.objName = objName;
     }
 }
diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/EntityReferences.java
similarity index 56%
copy from engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
copy to plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/EntityReferences.java
index 89e2c83..928e0da 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/EntityReferences.java
@@ -14,21 +14,26 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.storage.dao;
 
-import java.util.List;
-
-import com.cloud.storage.DiskOfferingVO;
-import com.cloud.utils.db.GenericDao;
+package org.apache.cloudstack.backup.veeam.api;
 
-public interface DiskOfferingDao extends GenericDao<DiskOfferingVO, Long> {
-
-    List<DiskOfferingVO> findPrivateDiskOffering();
+import java.util.List;
 
-    List<DiskOfferingVO> findPublicDiskOfferings();
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 
-    DiskOfferingVO findByUniqueName(String uniqueName);
+@JacksonXmlRootElement(localName = "EntityReferences")
+public class EntityReferences {
+    @JacksonXmlProperty(localName = "Ref")
+    @JacksonXmlElementWrapper(localName = "Ref", useWrapping = false)
+    private List<Ref> refs;
 
-    DiskOfferingVO persistDeafultDiskOffering(DiskOfferingVO offering);
+    public List<Ref> getRefs() {
+        return refs;
+    }
 
+    public void setRefs(List<Ref> refs) {
+        this.refs = refs;
+    }
 }
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/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/HierarchyItems.java
similarity index 55%
copy from vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java
copy to plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/HierarchyItems.java
index e2797d3..a87c6b0 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/HierarchyItems.java
@@ -14,28 +14,26 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.hypervisor.vmware.mo;
 
-import java.util.List;
+package org.apache.cloudstack.backup.veeam.api;
 
-import com.vmware.vim25.ManagedObjectReference;
+import java.util.List;
 
-import com.cloud.hypervisor.vmware.util.VmwareContext;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 
-public class NetworkMO extends BaseMO {
-    public NetworkMO(VmwareContext context, ManagedObjectReference morCluster) {
-        super(context, morCluster);
-    }
-
-    public NetworkMO(VmwareContext context, String morType, String morValue) {
-        super(context, morType, morValue);
-    }
+@JacksonXmlRootElement(localName = "HierarchyItems")
+public class HierarchyItems {
+    @JacksonXmlProperty(localName = "HierarchyItem")
+    @JacksonXmlElementWrapper(localName = "HierarchyItem", useWrapping = false)
+    private List<HierarchyItem> items;
 
-    public void destroyNetwork() throws Exception {
-        _context.getService().destroyNetwork(_mor);
+    public List<HierarchyItem> getItems() {
+        return items;
     }
 
-    public List<ManagedObjectReference> getVMsOnNetwork() throws Exception {
-        return _context.getVimClient().getDynamicProperty(_mor, "vm");
+    public void setItems(List<HierarchyItem> items) {
+        this.items = items;
     }
 }
diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Job.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Job.java
new file mode 100644
index 0000000..e42ecf0
--- /dev/null
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Job.java
@@ -0,0 +1,163 @@
+// 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 = "Job")
+public class Job {
+
+    @JacksonXmlProperty(localName = "Name", isAttribute = true)
+    private String name;
+
+    @JacksonXmlProperty(localName = "Href", isAttribute = true)
+    private String href;
+
+    @JacksonXmlProperty(localName = "Type", isAttribute = true)
+    private String type;
+
+    @JacksonXmlProperty(localName = "UID", isAttribute = true)
+    private String uid;
+
+    @JacksonXmlProperty(localName = "Link")
+    @JacksonXmlElementWrapper(localName = "Links")
+    private List<Link> link;
+
+    @JacksonXmlProperty(localName = "Platform")
+    private String platform;
+
+    @JacksonXmlProperty(localName = "Description")
+    private String description;
+
+    @JacksonXmlProperty(localName = "NextRun")
+    private String nextRun;
+
+    @JacksonXmlProperty(localName = "JobType")
+    private String jobType;
+
+    @JacksonXmlProperty(localName = "ScheduleEnabled")
+    private Boolean scheduleEnabled;
+
+    @JacksonXmlProperty(localName = "ScheduleConfigured")
+    private Boolean scheduleConfigured;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = 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 String getUid() {
+        return uid;
+    }
+
+    public String getId() {
+        return uid.replace("urn:veeam:Job:", "");
+    }
+
+    public void setUid(String uid) {
+        this.uid = uid;
+    }
+
+    public List<Link> getLink() {
+        return link;
+    }
+
+    public void setLink(List<Link> link) {
+        this.link = link;
+    }
+
+    public String getPlatform() {
+        return platform;
+    }
+
+    public void setPlatform(String platform) {
+        this.platform = platform;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getNextRun() {
+        return nextRun;
+    }
+
+    public void setNextRun(String nextRun) {
+        this.nextRun = nextRun;
+    }
+
+    public String getJobType() {
+        return jobType;
+    }
+
+    public void setJobType(String jobType) {
+        this.jobType = jobType;
+    }
+
+    public String getBackupServerId() {
+        for (final Link l : link) {
+            if (l.getType().equals("BackupServerReference")) {
+                return "" + l.getHref().split("backupServers/")[1];
+            }
+        }
+        return null;
+    }
+
+    public Boolean getScheduleEnabled() {
+        return scheduleEnabled;
+    }
+
+    public void setScheduleEnabled(String scheduleEnabled) {
+        this.scheduleEnabled = Boolean.valueOf(scheduleEnabled);
+    }
+
+    public Boolean getScheduleConfigured() {
+        return scheduleConfigured;
+    }
+
+    public void setScheduleConfigured(String scheduleConfigured) {
+        this.scheduleConfigured = Boolean.valueOf(scheduleConfigured);
+    }
+}
diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/JobCloneSpec.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/JobCloneSpec.java
new file mode 100644
index 0000000..fd1bdfb
--- /dev/null
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/JobCloneSpec.java
@@ -0,0 +1,41 @@
+// 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.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+
+@JacksonXmlRootElement(localName = "JobCloneSpec", namespace = "http://www.veeam.com/ent/v1.0")
+public class JobCloneSpec {
+    @JacksonXmlProperty(localName = "BackupJobCloneInfo")
+    @JacksonXmlElementWrapper(localName = "BackupJobCloneInfo", useWrapping = false)
+    BackupJobCloneInfo jobCloneInfo;
+
+    public JobCloneSpec(final BackupJobCloneInfo jobCloneInfo) {
+        this.jobCloneInfo = jobCloneInfo;
+    }
+
+    public BackupJobCloneInfo getJobCloneInfo() {
+        return jobCloneInfo;
+    }
+
+    public void setJobCloneInfo(BackupJobCloneInfo jobCloneInfo) {
+        this.jobCloneInfo = jobCloneInfo;
+    }
+}
diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Link.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Link.java
new file mode 100644
index 0000000..b89d77f
--- /dev/null
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Link.java
@@ -0,0 +1,69 @@
+// 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 = "Link")
+public class Link {
+
+    @JacksonXmlProperty(localName = "Name", isAttribute = true)
+    private String name;
+
+    @JacksonXmlProperty(localName = "Href", isAttribute = true)
+    private String href;
+
+    @JacksonXmlProperty(localName = "Type", isAttribute = true)
+    private String type;
+
+    @JacksonXmlProperty(localName = "Rel", isAttribute = true)
+    private String rel;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = 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 String getRel() {
+        return rel;
+    }
+
+    public void setRel(String rel) {
+        this.rel = rel;
+    }
+}
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/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/ObjectsInJob.java
similarity index 55%
copy from vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java
copy to plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/ObjectsInJob.java
index e2797d3..982dae5 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/ObjectsInJob.java
@@ -14,28 +14,26 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.hypervisor.vmware.mo;
 
-import java.util.List;
+package org.apache.cloudstack.backup.veeam.api;
 
-import com.vmware.vim25.ManagedObjectReference;
+import java.util.List;
 
-import com.cloud.hypervisor.vmware.util.VmwareContext;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 
-public class NetworkMO extends BaseMO {
-    public NetworkMO(VmwareContext context, ManagedObjectReference morCluster) {
-        super(context, morCluster);
-    }
-
-    public NetworkMO(VmwareContext context, String morType, String morValue) {
-        super(context, morType, morValue);
-    }
+@JacksonXmlRootElement(localName = "ObjectsInJob")
+public class ObjectsInJob {
+    @JacksonXmlProperty(localName = "ObjectInJob")
+    @JacksonXmlElementWrapper(localName = "ObjectInJob", useWrapping = false)
+    private List<ObjectInJob> objects;
 
-    public void destroyNetwork() throws Exception {
-        _context.getService().destroyNetwork(_mor);
+    public List<ObjectInJob> getObjects() {
+        return objects;
     }
 
-    public List<ManagedObjectReference> getVMsOnNetwork() throws Exception {
-        return _context.getVimClient().getDynamicProperty(_mor, "vm");
+    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
new file mode 100644
index 0000000..683fd37
--- /dev/null
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Ref.java
@@ -0,0 +1,83 @@
+// 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 = "Ref")
+public class Ref {
+    @JacksonXmlProperty(localName = "UID", isAttribute = true)
+    private String uid;
+
+    @JacksonXmlProperty(localName = "Name", isAttribute = true)
+    private String name;
+
+    @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;
+
+    public List<Link> getLink() {
+        return link;
+    }
+
+    public void setLink(List<Link> link) {
+        this.link = link;
+    }
+
+    public String getUid() {
+        return uid;
+    }
+
+    public void setUid(String uid) {
+        this.uid = uid;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = 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;
+    }
+}
diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/RestoreSession.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/RestoreSession.java
new file mode 100644
index 0000000..0675e49
--- /dev/null
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/RestoreSession.java
@@ -0,0 +1,120 @@
+// 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.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
+
+import java.util.List;
+
+@JacksonXmlRootElement(localName = "RestoreSession")
+public class RestoreSession {
+
+    @JacksonXmlProperty(localName = "Type", isAttribute = true)
+    private String type;
+
+    @JacksonXmlProperty(localName = "Href", isAttribute = true)
+    private String href;
+
+    @JacksonXmlProperty(localName = "Name", isAttribute = true)
+    private String name;
+
+    @JacksonXmlProperty(localName = "UID", isAttribute = true)
+    private String uid;
+
+    @JacksonXmlProperty(localName = "VmDisplayName", isAttribute = true)
+    private String vmDisplayName;
+
+    @JacksonXmlProperty(localName = "Link")
+    @JacksonXmlElementWrapper(localName = "Links")
+    private List<Link> link;
+
+    @JacksonXmlProperty(localName = "JobType")
+    private String jobType;
+
+    @JacksonXmlProperty(localName = "CreationTimeUTC")
+    private String creationTimeUTC;
+
+    @JacksonXmlProperty(localName = "EndTimeUTC")
+    private String endTimeUTC;
+
+    @JacksonXmlProperty(localName = "State")
+    private String state;
+
+    @JacksonXmlProperty(localName = "Result")
+    private String result;
+
+    @JacksonXmlProperty(localName = "Progress")
+    private String progress;
+
+    @JacksonXmlProperty(localName = "RestoredObjRef")
+    private String restoredObjRef;
+
+    public List<Link> getLink() {
+        return link;
+    }
+
+    public String getJobType() {
+        return jobType;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    public String getResult() {
+        return result;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public String getHref() {
+        return href;
+    }
+
+    public String getVmDisplayName() {
+        return vmDisplayName;
+    }
+
+    public String getCreationTimeUTC() {
+        return creationTimeUTC;
+    }
+
+    public String getEndTimeUTC() {
+        return endTimeUTC;
+    }
+
+    public String getProgress() {
+        return progress;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getUid() {
+        return uid;
+    }
+
+    public String getRestoredObjRef() {
+        return restoredObjRef;
+    }
+}
diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Result.java
similarity index 54%
copy from vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java
copy to plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Result.java
index e2797d3..26fc863 100644
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/NetworkMO.java
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Result.java
@@ -14,28 +14,34 @@
 // KIND, either express or implied.  See the License for the
 // specific language governing permissions and limitations
 // under the License.
-package com.cloud.hypervisor.vmware.mo;
 
-import java.util.List;
+package org.apache.cloudstack.backup.veeam.api;
 
-import com.vmware.vim25.ManagedObjectReference;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 
-import com.cloud.hypervisor.vmware.util.VmwareContext;
+@JacksonXmlRootElement(localName = "Result")
+public class Result {
 
-public class NetworkMO extends BaseMO {
-    public NetworkMO(VmwareContext context, ManagedObjectReference morCluster) {
-        super(context, morCluster);
+    @JacksonXmlProperty(localName = "Success", isAttribute = true)
+    private String success;
+
+    @JacksonXmlProperty(localName = "Message")
+    private String message;
+
+    public String getSuccess() {
+        return success;
     }
 
-    public NetworkMO(VmwareContext context, String morType, String morValue) {
-        super(context, morType, morValue);
+    public void setSuccess(String success) {
+        this.success = success;
     }
 
-    public void destroyNetwork() throws Exception {
-        _context.getService().destroyNetwork(_mor);
+    public String getMessage() {
+        return message;
     }
 
-    public List<ManagedObjectReference> getVMsOnNetwork() throws Exception {
-        return _context.getVimClient().getDynamicProperty(_mor, "vm");
+    public void setMessage(String message) {
+        this.message = message;
     }
 }
diff --git a/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Task.java b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Task.java
new file mode 100644
index 0000000..e8d14e5
--- /dev/null
+++ b/plugins/backup/veeam/src/main/java/org/apache/cloudstack/backup/veeam/api/Task.java
@@ -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;
+    }
+}
diff --git a/plugins/backup/veeam/src/main/resources/META-INF/cloudstack/veeam/module.properties b/plugins/backup/veeam/src/main/resources/META-INF/cloudstack/veeam/module.properties
new file mode 100644
index 0000000..ee40b21
--- /dev/null
+++ b/plugins/backup/veeam/src/main/resources/META-INF/cloudstack/veeam/module.properties
@@ -0,0 +1,18 @@
+# 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.
+name=veeam
+parent=backup
diff --git a/plugins/backup/veeam/src/main/resources/META-INF/cloudstack/veeam/spring-backup-veeam-context.xml b/plugins/backup/veeam/src/main/resources/META-INF/cloudstack/veeam/spring-backup-veeam-context.xml
new file mode 100644
index 0000000..f2403cf
--- /dev/null
+++ b/plugins/backup/veeam/src/main/resources/META-INF/cloudstack/veeam/spring-backup-veeam-context.xml
@@ -0,0 +1,27 @@
+<!--
+  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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
+
+    <bean id="veeamBackupProvider" class="org.apache.cloudstack.backup.VeeamBackupProvider">
+        <property name="name" value="veeam" />
+    </bean>
+</beans>
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
new file mode 100644
index 0000000..3af81d0
--- /dev/null
+++ b/plugins/backup/veeam/src/test/java/org/apache/cloudstack/backup/veeam/VeeamClientTest.java
@@ -0,0 +1,85 @@
+// 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;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+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 java.util.List;
+
+import org.apache.cloudstack.backup.BackupOffering;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import com.github.tomakehurst.wiremock.client.BasicCredentials;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+
+public class VeeamClientTest {
+
+    private String adminUsername = "administrator";
+    private String adminPassword = "password";
+    private VeeamClient client;
+
+    @Rule
+    public WireMockRule wireMockRule = new WireMockRule(9399);
+
+    @Before
+    public void setUp() throws Exception {
+        wireMockRule.stubFor(post(urlMatching(".*/sessionMngr/.*"))
+                .willReturn(aResponse()
+                        .withStatus(201)
+                        .withHeader("X-RestSvcSessionId", "some-session-auth-id")
+                        .withBody("")));
+        client = new VeeamClient("http://localhost:9399/api/", adminUsername, adminPassword, true, 60);
+    }
+
+    @Test
+    public void testBasicAuth() {
+        verify(postRequestedFor(urlMatching(".*/sessionMngr/.*"))
+                .withBasicAuth(new BasicCredentials(adminUsername, adminPassword)));
+    }
+
+    @Test
+    public void testVeeamJobs() {
+        wireMockRule.stubFor(get(urlMatching(".*/jobs"))
+                .willReturn(aResponse()
+                        .withHeader("Content-Type", "application/xml")
+                        .withStatus(200)
+                        .withBody("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
+                                "<EntityReferences xmlns=\"http://www.veeam.com/ent/v1.0\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" +
+                                "    <Ref UID=\"urn:veeam:Job:8acac50d-3711-4c99-bf7b-76fe9c7e39c3\" Name=\"ZONE1-GOLD\" Href=\"http://10.1.1.10:9399/api/jobs/8acac50d-3711-4c99-bf7b-76fe9c7e39c3\" Type=\"JobReference\">\n" +
+                                "        <Links>\n" +
+                                "            <Link Href=\"http://10.1.1.10:9399/api/backupServers/1efaeae4-d23c-46cd-84a1-8798f68bdb78\" Name=\"10.1.1.10\" Type=\"BackupServerReference\" Rel=\"Up\"/>\n" +
+                                "            <Link Href=\"http://10.1.1.10:9399/api/jobs/8acac50d-3711-4c99-bf7b-76fe9c7e39c3?format=Entity\" Name=\"ZONE1-GOLD\" Type=\"Job\" Rel=\"Alternate\"/>\n" +
+                                "            <Link Href=\"http://10.1.1.10:9399/api/jobs/8acac50d-3711-4c99-bf7b-76fe9c7e39c3/backupSessions\" Type=\"BackupJobSessionReferenceList\" Rel=\"Down\"/>\n" +
+                                "        </Links>\n" +
+                                "    </Ref>\n" +
+                                "</EntityReferences>")));
+        List<BackupOffering> policies = client.listJobs();
+        verify(getRequestedFor(urlMatching(".*/jobs")));
+        Assert.assertEquals(policies.size(), 1);
+        Assert.assertEquals(policies.get(0).getName(), "ZONE1-GOLD");
+    }
+}
\ No newline at end of file
diff --git a/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/SimulatorGuru.java b/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/SimulatorGuru.java
index 4516c7c..246d86d 100644
--- a/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/SimulatorGuru.java
+++ b/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/SimulatorGuru.java
@@ -16,22 +16,41 @@
 // under the License.
 package com.cloud.simulator;
 
+import java.util.Date;
 import java.util.Map;
 
 import javax.inject.Inject;
 
+import org.apache.cloudstack.backup.Backup;
+
 import com.cloud.agent.api.to.VirtualMachineTO;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.hypervisor.HypervisorGuru;
 import com.cloud.hypervisor.HypervisorGuruBase;
 import com.cloud.storage.GuestOSVO;
+import com.cloud.storage.Volume;
+import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.GuestOSDao;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineProfile;
+import com.cloud.vm.dao.NicDao;
+import com.cloud.vm.dao.VMInstanceDao;
 
 public class SimulatorGuru extends HypervisorGuruBase implements HypervisorGuru {
     @Inject
     GuestOSDao _guestOsDao;
 
+    @Inject
+    VMInstanceDao instanceDao;
+
+    @Inject
+    VolumeDao volumeDao;
+
+    @Inject
+    NicDao nicDao;
+
     protected SimulatorGuru() {
         super();
     }
@@ -53,6 +72,25 @@ public class SimulatorGuru extends HypervisorGuruBase implements HypervisorGuru
     }
 
     @Override
+    public VirtualMachine importVirtualMachineFromBackup(long zoneId, long domainId, long accountId, long userId,
+                                                  String vmInternalName, Backup backup) {
+        VMInstanceVO vm = instanceDao.findVMByInstanceNameIncludingRemoved(vmInternalName);
+        if (vm.getRemoved() != null) {
+            vm.setState(VirtualMachine.State.Stopped);
+            vm.setPowerState(VirtualMachine.PowerState.PowerOff);
+            instanceDao.update(vm.getId(), vm);
+            instanceDao.unremove(vm.getId());
+        }
+        for (final VolumeVO volume : volumeDao.findIncludingRemovedByInstanceAndType(vm.getId(), null)) {
+            volume.setState(Volume.State.Ready);
+            volume.setAttached(new Date());
+            volumeDao.update(volume.getId(), volume);
+            volumeDao.unremove(volume.getId());
+        }
+        return vm;
+    }
+
+    @Override
     public boolean trackVmHostChange() {
         return false;
     }
diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
index 072ab9f..39c1e66 100644
--- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
+++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
@@ -19,6 +19,7 @@ package com.cloud.hypervisor.guru;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -27,32 +28,26 @@ import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 
-import com.cloud.agent.api.MigrateVmToPoolCommand;
-import com.cloud.agent.api.UnregisterVMCommand;
-import com.cloud.agent.api.storage.OVFPropertyTO;
-import com.cloud.agent.api.to.VolumeTO;
-import com.cloud.dc.ClusterDetailsDao;
-import com.cloud.storage.StoragePool;
-import com.cloud.storage.TemplateOVFPropertyVO;
-import com.cloud.storage.VMTemplateStoragePoolVO;
-import com.cloud.storage.VMTemplateStorageResourceAssoc;
-import com.cloud.storage.dao.TemplateOVFPropertiesDao;
-import com.cloud.storage.dao.VMTemplatePoolDao;
+import org.apache.cloudstack.acl.ControlledEntity;
 import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
 import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
 import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.framework.config.Configurable;
 import org.apache.cloudstack.storage.command.CopyCommand;
-import org.apache.cloudstack.storage.command.DownloadCommand;
 import org.apache.cloudstack.storage.command.DeleteCommand;
+import org.apache.cloudstack.storage.command.DownloadCommand;
 import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
 import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 import org.apache.cloudstack.storage.to.VolumeObjectTO;
+import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.BooleanUtils;
+import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
 
 import com.cloud.agent.api.BackupSnapshotCommand;
@@ -60,10 +55,13 @@ import com.cloud.agent.api.Command;
 import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
 import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
 import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
+import com.cloud.agent.api.MigrateVmToPoolCommand;
 import com.cloud.agent.api.UnregisterNicCommand;
+import com.cloud.agent.api.UnregisterVMCommand;
 import com.cloud.agent.api.storage.CopyVolumeCommand;
 import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand;
 import com.cloud.agent.api.storage.CreateVolumeOVACommand;
+import com.cloud.agent.api.storage.OVFPropertyTO;
 import com.cloud.agent.api.storage.PrepareOVAPackingCommand;
 import com.cloud.agent.api.to.DataObjectType;
 import com.cloud.agent.api.to.DataStoreTO;
@@ -71,8 +69,14 @@ import com.cloud.agent.api.to.DataTO;
 import com.cloud.agent.api.to.DiskTO;
 import com.cloud.agent.api.to.NicTO;
 import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.agent.api.to.VolumeTO;
 import com.cloud.cluster.ClusterManager;
+import com.cloud.configuration.Resource;
+import com.cloud.dc.ClusterDetailsDao;
+import com.cloud.event.EventTypes;
+import com.cloud.event.UsageEventUtils;
 import com.cloud.exception.InsufficientAddressCapacityException;
+import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.host.Host;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
@@ -80,32 +84,61 @@ import com.cloud.host.dao.HostDetailsDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.hypervisor.HypervisorGuru;
 import com.cloud.hypervisor.HypervisorGuruBase;
+import com.cloud.hypervisor.vmware.VmwareDatacenterVO;
+import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMapVO;
+import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao;
+import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao;
 import com.cloud.hypervisor.vmware.manager.VmwareManager;
+import com.cloud.hypervisor.vmware.mo.DatacenterMO;
 import com.cloud.hypervisor.vmware.mo.DiskControllerType;
+import com.cloud.hypervisor.vmware.mo.NetworkMO;
+import com.cloud.hypervisor.vmware.mo.VirtualDiskManagerMO;
 import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType;
+import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder;
+import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
+import com.cloud.hypervisor.vmware.resource.VmwareContextFactory;
+import com.cloud.hypervisor.vmware.util.VmwareContext;
+import com.cloud.network.Network;
 import com.cloud.network.Network.Provider;
 import com.cloud.network.Network.Service;
 import com.cloud.network.NetworkModel;
+import com.cloud.network.Networks;
 import com.cloud.network.Networks.BroadcastDomainType;
 import com.cloud.network.Networks.TrafficType;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
+import com.cloud.network.dao.PhysicalNetworkDao;
 import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao;
 import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO;
+import com.cloud.network.dao.PhysicalNetworkVO;
 import com.cloud.secstorage.CommandExecLogDao;
 import com.cloud.secstorage.CommandExecLogVO;
+import com.cloud.service.ServiceOfferingVO;
+import com.cloud.service.dao.ServiceOfferingDao;
 import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.GuestOSHypervisorVO;
 import com.cloud.storage.GuestOSVO;
 import com.cloud.storage.Storage;
+import com.cloud.storage.StoragePool;
+import com.cloud.storage.TemplateOVFPropertyVO;
+import com.cloud.storage.VMTemplateStoragePoolVO;
+import com.cloud.storage.VMTemplateStorageResourceAssoc;
+import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.Volume;
 import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.DiskOfferingDao;
 import com.cloud.storage.dao.GuestOSDao;
 import com.cloud.storage.dao.GuestOSHypervisorDao;
+import com.cloud.storage.dao.TemplateOVFPropertiesDao;
+import com.cloud.storage.dao.VMTemplateDao;
+import com.cloud.storage.dao.VMTemplatePoolDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.storage.secondary.SecondaryStorageVmManager;
 import com.cloud.template.VirtualMachineTemplate.BootloaderType;
+import com.cloud.user.ResourceLimitService;
 import com.cloud.utils.Pair;
+import com.cloud.utils.UuidUtils;
 import com.cloud.utils.db.DB;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.net.NetUtils;
@@ -113,13 +146,28 @@ import com.cloud.vm.DomainRouterVO;
 import com.cloud.vm.NicProfile;
 import com.cloud.vm.NicVO;
 import com.cloud.vm.SecondaryStorageVmVO;
+import com.cloud.vm.UserVmVO;
+import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.Type;
+import com.cloud.vm.VirtualMachineManager;
 import com.cloud.vm.VirtualMachineProfile;
 import com.cloud.vm.VmDetailConstants;
 import com.cloud.vm.dao.DomainRouterDao;
 import com.cloud.vm.dao.NicDao;
+import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.VMInstanceDao;
+import com.google.gson.Gson;
+import com.vmware.vim25.ManagedObjectReference;
+import com.vmware.vim25.VirtualDevice;
+import com.vmware.vim25.VirtualDeviceBackingInfo;
+import com.vmware.vim25.VirtualDeviceConnectInfo;
+import com.vmware.vim25.VirtualDisk;
+import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
+import com.vmware.vim25.VirtualE1000;
+import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo;
+import com.vmware.vim25.VirtualMachineConfigSummary;
+import com.vmware.vim25.VirtualMachineRuntimeInfo;
 
 public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Configurable {
     private static final Logger s_logger = Logger.getLogger(VMwareGuru.class);
@@ -153,15 +201,33 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
     @Inject
     private VMInstanceDao _vmDao;
     @Inject
+    private VirtualMachineManager vmManager;
+    @Inject
     private ClusterManager _clusterMgr;
     @Inject
     VolumeDao _volumeDao;
     @Inject
+    ResourceLimitService _resourceLimitService;
+    @Inject
     PrimaryDataStoreDao _storagePoolDao;
     @Inject
     VolumeDataFactory _volFactory;
     @Inject
-    private VMTemplatePoolDao templateSpoolDao;
+    private VmwareDatacenterDao vmwareDatacenterDao;
+    @Inject
+    private VmwareDatacenterZoneMapDao vmwareDatacenterZoneMapDao;
+    @Inject
+    private ServiceOfferingDao serviceOfferingDao;
+    @Inject
+    private VMTemplatePoolDao templateStoragePoolDao;
+    @Inject
+    private VMTemplateDao vmTemplateDao;
+    @Inject
+    private UserVmDao userVmDao;
+    @Inject
+    private DiskOfferingDao diskOfferingDao;
+    @Inject
+    private PhysicalNetworkDao physicalNetworkDao;
     @Inject
     private TemplateOVFPropertiesDao templateOVFPropertiesDao;
 
@@ -398,7 +464,7 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
             long dataCenterId = storagePoolVO.getDataCenterId();
             List<StoragePoolVO> pools = _storagePoolDao.listByDataCenterId(dataCenterId);
             for (StoragePoolVO pool : pools) {
-                VMTemplateStoragePoolVO ref = templateSpoolDao.findByPoolTemplate(pool.getId(), vm.getTemplateId());
+                VMTemplateStoragePoolVO ref = templateStoragePoolDao.findByPoolTemplate(pool.getId(), vm.getTemplateId());
                 if (ref != null && ref.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED) {
                     templateInstallPath = ref.getInstallPath();
                     break;
@@ -718,6 +784,671 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co
         return details;
     }
 
+    /**
+     * Get vmware datacenter mapped to the zoneId
+     */
+    private VmwareDatacenterVO getVmwareDatacenter(long zoneId) {
+        VmwareDatacenterZoneMapVO zoneMap = vmwareDatacenterZoneMapDao.findByZoneId(zoneId);
+        long vmwareDcId = zoneMap.getVmwareDcId();
+        return vmwareDatacenterDao.findById(vmwareDcId);
+    }
+
+    /**
+     * Get Vmware datacenter MO
+     */
+    private DatacenterMO getDatacenterMO(long zoneId) throws Exception {
+        VmwareDatacenterVO vmwareDatacenter = getVmwareDatacenter(zoneId);
+        VmwareContext context = VmwareContextFactory.getContext(vmwareDatacenter.getVcenterHost(),
+                vmwareDatacenter.getUser(), vmwareDatacenter.getPassword());
+        DatacenterMO dcMo = new DatacenterMO(context, vmwareDatacenter.getVmwareDatacenterName());
+        ManagedObjectReference dcMor = dcMo.getMor();
+        if (dcMor == null) {
+            String msg = "Error while getting Vmware datacenter " + vmwareDatacenter.getVmwareDatacenterName();
+            s_logger.error(msg);
+            throw new InvalidParameterValueException(msg);
+        }
+        return dcMo;
+    }
+
+    /**
+     * Get guest OS ID for VM being imported.
+     * If it cannot be found it is mapped to: "Other (64-bit)" ID
+     */
+    private Long getImportingVMGuestOs(VirtualMachineConfigSummary configSummary) {
+        String guestFullName = configSummary.getGuestFullName();
+        GuestOSVO os = _guestOsDao.listByDisplayName(guestFullName);
+        return os != null ? os.getId() : _guestOsDao.listByDisplayName("Other (64-bit)").getId();
+    }
+
+    /**
+     * Create and persist service offering
+     */
+    private ServiceOfferingVO createServiceOfferingForVMImporting(Integer cpus, Integer memory, Integer maxCpuUsage) {
+        String name = "Imported-" + cpus + "-" + memory;
+        ServiceOfferingVO vo = new ServiceOfferingVO(name, cpus, memory, maxCpuUsage, null, null,
+                false, name, Storage.ProvisioningType.THIN, false, false,
+                null, false, Type.User, false);
+        return serviceOfferingDao.persist(vo);
+    }
+
+    /**
+     * Get service offering ID for VM being imported.
+     * If it cannot be found it creates one and returns its ID
+     */
+    private Long getImportingVMServiceOffering(VirtualMachineConfigSummary configSummary,
+                                               VirtualMachineRuntimeInfo runtimeInfo) {
+        Integer numCpu = configSummary.getNumCpu();
+        Integer memorySizeMB = configSummary.getMemorySizeMB();
+        Integer maxCpuUsage = runtimeInfo.getMaxCpuUsage();
+        List<ServiceOfferingVO> offerings = serviceOfferingDao.listPublicByCpuAndMemory(numCpu, memorySizeMB);
+        return CollectionUtils.isEmpty(offerings) ?
+                createServiceOfferingForVMImporting(numCpu, memorySizeMB, maxCpuUsage).getId() :
+                offerings.get(0).getId();
+    }
+
+    /**
+     * Check if disk is ROOT disk
+     */
+    private boolean isRootDisk(VirtualDisk disk, Map<VirtualDisk, VolumeVO> disksMapping, Backup backup) {
+        if (!disksMapping.containsKey(disk)) {
+            return false;
+        }
+        VolumeVO volumeVO = disksMapping.get(disk);
+        if (volumeVO == null) {
+            final VMInstanceVO vm = _vmDao.findByIdIncludingRemoved(backup.getVmId());
+            if (vm == null) {
+                throw new CloudRuntimeException("Failed to find the volumes details from the VM backup");
+            }
+            List<Backup.VolumeInfo> backedUpVolumes = vm.getBackupVolumeList();
+            for (Backup.VolumeInfo backedUpVolume : backedUpVolumes) {
+                if (backedUpVolume.getSize().equals(disk.getCapacityInBytes())) {
+                    return backedUpVolume.getType().equals(Volume.Type.ROOT);
+                }
+            }
+        } else {
+            return volumeVO.getVolumeType().equals(Volume.Type.ROOT);
+        }
+        throw new CloudRuntimeException("Could not determinate ROOT disk for VM to import");
+    }
+
+    /**
+     * Check backing info
+     */
+    private void checkBackingInfo(VirtualDeviceBackingInfo backingInfo) {
+        if (! (backingInfo instanceof VirtualDiskFlatVer2BackingInfo)) {
+            throw new CloudRuntimeException("Unsopported backing, expected " + VirtualDiskFlatVer2BackingInfo.class.getSimpleName());
+        }
+    }
+
+    /**
+     * Get pool ID from datastore UUID
+     */
+    private Long getPoolIdFromDatastoreUuid(String datastoreUuid) {
+        String poolUuid = UuidUtils.normalize(datastoreUuid);
+        StoragePoolVO pool = _storagePoolDao.findByUuid(poolUuid);
+        if (pool == null) {
+            throw new CloudRuntimeException("Couldn't find storage pool " + poolUuid);
+        }
+        return pool.getId();
+    }
+
+    /**
+     * Get pool ID for disk
+     */
+    private Long getPoolId(VirtualDisk disk) {
+        VirtualDeviceBackingInfo backing = disk.getBacking();
+        checkBackingInfo(backing);
+        VirtualDiskFlatVer2BackingInfo info = (VirtualDiskFlatVer2BackingInfo) backing;
+        String[] fileNameParts = info.getFileName().split(" ");
+        String datastoreUuid = StringUtils.substringBetween(fileNameParts[0], "[", "]");
+        return getPoolIdFromDatastoreUuid(datastoreUuid);
+    }
+
+    /**
+     * Get volume name from filename
+     */
+    private String getVolumeNameFromFileName(String fileName) {
+        String[] fileNameParts = fileName.split(" ");
+        String volumePath = fileNameParts[1];
+        return volumePath.split("/")[1].replaceFirst(".vmdk", "");
+    }
+
+    /**
+     * Get root disk template path
+     */
+    private String getRootDiskTemplatePath(VirtualDisk rootDisk) {
+        VirtualDeviceBackingInfo backing = rootDisk.getBacking();
+        checkBackingInfo(backing);
+        VirtualDiskFlatVer2BackingInfo info = (VirtualDiskFlatVer2BackingInfo) backing;
+        VirtualDiskFlatVer2BackingInfo parent = info.getParent();
+        return (parent != null) ? getVolumeNameFromFileName(parent.getFileName()) : getVolumeNameFromFileName(info.getFileName());
+    }
+
+    /**
+     * Get template MO
+     */
+    private VirtualMachineMO getTemplate(DatacenterMO dcMo, String templatePath) throws Exception {
+        VirtualMachineMO template = dcMo.findVm(templatePath);
+        if (!template.isTemplate()) {
+            throw new CloudRuntimeException(templatePath + " is not a template");
+        }
+        return template;
+    }
+
+    /**
+     * Get template pool ID
+     */
+    private Long getTemplatePoolId(VirtualMachineMO template) throws Exception {
+        VirtualMachineConfigSummary configSummary = template.getConfigSummary();
+        String vmPathName = configSummary.getVmPathName();
+        String[] pathParts = vmPathName.split(" ");
+        String dataStoreUuid = pathParts[0].replace("[", "").replace("]", "");
+        return getPoolIdFromDatastoreUuid(dataStoreUuid);
+    }
+
+    /**
+     * Get template size
+     */
+    private Long getTemplateSize(VirtualMachineMO template, String vmInternalName,
+                                 Map<VirtualDisk, VolumeVO> disksMapping, Backup backup) throws Exception {
+        List<VirtualDisk> disks = template.getVirtualDisks();
+        if (CollectionUtils.isEmpty(disks)) {
+            throw new CloudRuntimeException("Couldn't find VM template size");
+        }
+        return disks.get(0).getCapacityInBytes();
+    }
+
+    /**
+     * Create a VM Template record on DB
+     */
+    private VMTemplateVO createVMTemplateRecord(String vmInternalName, long guestOsId, long accountId) {
+        Long nextTemplateId = vmTemplateDao.getNextInSequence(Long.class, "id");
+        VMTemplateVO templateVO = new VMTemplateVO(nextTemplateId, "Imported-from-" + vmInternalName,
+                Storage.ImageFormat.OVA,false, false, false, Storage.TemplateType.USER,
+                null, false, 64, accountId, null, "Template imported from VM " + vmInternalName,
+                false, guestOsId, false, HypervisorType.VMware, null, null,
+                false, false, false);
+        return vmTemplateDao.persist(templateVO);
+    }
+
+    /**
+     * Retrieve the template ID matching the template on templatePath. There are 2 cases:
+     * - There are no references on DB for primary storage -> create a template DB record and return its ID
+     * - There are references on DB for primary storage -> return template ID for any of those references
+     */
+    private long getTemplateId(String templatePath, String vmInternalName, Long guestOsId, long accountId) {
+        List<VMTemplateStoragePoolVO> poolRefs = templateStoragePoolDao.listByTemplatePath(templatePath);
+        return CollectionUtils.isNotEmpty(poolRefs) ?
+                poolRefs.get(0).getTemplateId() :
+                createVMTemplateRecord(vmInternalName, guestOsId, accountId).getId();
+    }
+
+    /**
+     * Update template reference on primary storage, if needed
+     */
+    private void updateTemplateRef(long templateId, Long poolId, String templatePath, Long templateSize) {
+        VMTemplateStoragePoolVO templateRef = templateStoragePoolDao.findByPoolPath(poolId, templatePath);
+        if (templateRef == null) {
+            templateRef = new VMTemplateStoragePoolVO(poolId, templateId, null, 100,
+                    VMTemplateStorageResourceAssoc.Status.DOWNLOADED, templatePath, null,
+                    null, templatePath, templateSize);
+            templateRef.setState(ObjectInDataStoreStateMachine.State.Ready);
+            templateStoragePoolDao.persist(templateRef);
+        }
+    }
+
+    /**
+     * Get template ID for VM being imported. If it is not found, it is created
+     */
+    private Long getImportingVMTemplate(List<VirtualDisk> virtualDisks, DatacenterMO dcMo, String vmInternalName,
+                                        Long guestOsId, long accountId, Map<VirtualDisk, VolumeVO> disksMapping, Backup backup) throws Exception {
+        for (VirtualDisk disk : virtualDisks) {
+            if (isRootDisk(disk, disksMapping, backup)) {
+                VolumeVO volumeVO = disksMapping.get(disk);
+                if (volumeVO == null) {
+                    String templatePath = getRootDiskTemplatePath(disk);
+                    VirtualMachineMO template = getTemplate(dcMo, templatePath);
+                    Long poolId = getTemplatePoolId(template);
+                    Long templateSize = getTemplateSize(template, vmInternalName, disksMapping, backup);
+                    long templateId = getTemplateId(templatePath, vmInternalName, guestOsId, accountId);
+                    updateTemplateRef(templateId, poolId, templatePath, templateSize);
+                    return templateId;
+                } else {
+                    return volumeVO.getTemplateId();
+                }
+            }
+        }
+        throw new CloudRuntimeException("Could not find ROOT disk for VM " + vmInternalName);
+    }
+
+    /**
+     * If VM does not exist: create and persist VM
+     * If VM exists: update VM
+     */
+    private VMInstanceVO getVM(String vmInternalName, long templateId, long guestOsId,
+                               long serviceOfferingId, long zoneId, long accountId, long userId,
+                               long domainId) {
+        VMInstanceVO vm = _vmDao.findVMByInstanceNameIncludingRemoved(vmInternalName);
+        if (vm != null) {
+            vm.setState(VirtualMachine.State.Stopped);
+            vm.setPowerState(VirtualMachine.PowerState.PowerOff);
+            _vmDao.update(vm.getId(), vm);
+            if (vm.getRemoved() != null) {
+                _vmDao.unremove(vm.getId());
+                UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, vm.getDataCenterId(), vm.getId(),
+                        vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(),
+                        vm.getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplayVm());
+            }
+            return _vmDao.findById(vm.getId());
+        } else {
+            long id = userVmDao.getNextInSequence(Long.class, "id");
+            UserVmVO vmInstanceVO = new UserVmVO(id, vmInternalName, vmInternalName, templateId, HypervisorType.VMware,
+                    guestOsId, false, false, domainId, accountId, userId, serviceOfferingId,
+                    null, vmInternalName, null);
+            vmInstanceVO.setDataCenterId(zoneId);
+            return userVmDao.persist(vmInstanceVO);
+        }
+    }
+
+    /**
+     * Create and persist volume
+     */
+    private VolumeVO createVolumeRecord(Volume.Type type, String volumeName, long zoneId, long domainId,
+                                        long accountId, long diskOfferingId, Storage.ProvisioningType provisioningType,
+                                        Long size, long instanceId, Long poolId, long templateId, Integer unitNumber, VirtualMachineDiskInfo diskInfo) {
+        VolumeVO volumeVO = new VolumeVO(type, volumeName, zoneId, domainId, accountId, diskOfferingId,
+                provisioningType, size, null, null, null);
+        volumeVO.setFormat(Storage.ImageFormat.OVA);
+        volumeVO.setPath(volumeName);
+        volumeVO.setState(Volume.State.Ready);
+        volumeVO.setInstanceId(instanceId);
+        volumeVO.setPoolId(poolId);
+        volumeVO.setTemplateId(templateId);
+        volumeVO.setAttached(new Date());
+        volumeVO.setRemoved(null);
+        volumeVO.setChainInfo(new Gson().toJson(diskInfo));
+        if (unitNumber != null) {
+            volumeVO.setDeviceId(unitNumber.longValue());
+        }
+        return _volumeDao.persist(volumeVO);
+    }
+
+    /**
+     * Get volume name from VM disk
+     */
+    private String getVolumeName(VirtualDisk disk, VirtualMachineMO vmToImport) throws Exception {
+        return vmToImport.getVmdkFileBaseName(disk);
+    }
+
+    /**
+     * Get provisioning type for VM disk info
+     */
+    private Storage.ProvisioningType getProvisioningType(VirtualDiskFlatVer2BackingInfo backing) {
+        Boolean thinProvisioned = backing.isThinProvisioned();
+        if (BooleanUtils.isTrue(thinProvisioned)) {
+            return Storage.ProvisioningType.THIN;
+        }
+        return Storage.ProvisioningType.SPARSE;
+    }
+
+    /**
+     * Get disk offering ID for volume being imported. If it is not found it is mapped to "Custom" ID
+     */
+    private long getDiskOfferingId(long size, Storage.ProvisioningType provisioningType) {
+        List<DiskOfferingVO> offerings = diskOfferingDao.listAllBySizeAndProvisioningType(size, provisioningType);
+        return CollectionUtils.isNotEmpty(offerings) ?
+                offerings.get(0).getId() :
+                diskOfferingDao.findByUniqueName("Cloud.Com-Custom").getId();
+    }
+
+    protected VolumeVO updateVolume(VirtualDisk disk, Map<VirtualDisk, VolumeVO> disksMapping, VirtualMachineMO vmToImport, Long poolId, VirtualMachine vm) throws Exception {
+        VolumeVO volume = disksMapping.get(disk);
+        String volumeName = getVolumeName(disk, vmToImport);
+        volume.setPath(volumeName);
+        volume.setPoolId(poolId);
+        VirtualMachineDiskInfo diskInfo = getDiskInfo(vmToImport, poolId, volumeName);
+        volume.setChainInfo(new Gson().toJson(diskInfo));
+        volume.setInstanceId(vm.getId());
+        volume.setState(Volume.State.Ready);
+        volume.setAttached(new Date());
+        _volumeDao.update(volume.getId(), volume);
+        if (volume.getRemoved() != null) {
+            _volumeDao.unremove(volume.getId());
+            if (vm.getType() == Type.User) {
+                UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(),
+                        volume.getId(), volume.getName(), volume.getDiskOfferingId(), null, volume.getSize(),
+                        Volume.class.getName(), volume.getUuid(), volume.isDisplayVolume());
+                _resourceLimitService.incrementResourceCount(vm.getAccountId(), Resource.ResourceType.volume, volume.isDisplayVolume());
+                _resourceLimitService.incrementResourceCount(vm.getAccountId(), Resource.ResourceType.primary_storage, volume.isDisplayVolume(), volume.getSize());
+            }
+        }
+        return volume;
+    }
+
+    /**
+     * Get volumes for VM being imported
+     */
+    private void syncVMVolumes(VMInstanceVO vmInstanceVO, List<VirtualDisk> virtualDisks,
+                               Map<VirtualDisk, VolumeVO> disksMapping, VirtualMachineMO vmToImport, Backup backup) throws Exception {
+        long zoneId = vmInstanceVO.getDataCenterId();
+        long accountId = vmInstanceVO.getAccountId();
+        long domainId = vmInstanceVO.getDomainId();
+        long templateId = vmInstanceVO.getTemplateId();
+        long instanceId = vmInstanceVO.getId();
+
+        for (VirtualDisk disk : virtualDisks) {
+            Long poolId = getPoolId(disk);
+            Volume volume = null;
+            if (disksMapping.containsKey(disk) && disksMapping.get(disk) != null) {
+                volume = updateVolume(disk, disksMapping, vmToImport, poolId, vmInstanceVO);
+            } else {
+                volume = createVolume(disk, vmToImport, domainId, zoneId, accountId, instanceId, poolId, templateId, backup, true);
+            }
+            s_logger.debug("VM backup restored (updated/created) volume id:" + volume.getId() + " for VM id:" + instanceId);
+        }
+    }
+
+    private VirtualMachineDiskInfo getDiskInfo(VirtualMachineMO vmMo, Long poolId, String volumeName) throws Exception {
+        VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder();
+        String poolName = _storagePoolDao.findById(poolId).getUuid().replace("-", "");
+        return diskInfoBuilder.getDiskInfoByBackingFileBaseName(volumeName, poolName);
+    }
+
+    private VolumeVO createVolume(VirtualDisk disk, VirtualMachineMO vmToImport, long domainId, long zoneId,
+                                  long accountId, long instanceId, Long poolId, long templateId, Backup backup, boolean isImport) throws Exception {
+        VMInstanceVO vm = _vmDao.findByIdIncludingRemoved(backup.getVmId());
+        if (vm == null) {
+            throw new CloudRuntimeException("Failed to find the backup volume information from the VM backup");
+        }
+        List<Backup.VolumeInfo> backedUpVolumes = vm.getBackupVolumeList();
+        Volume.Type type = Volume.Type.DATADISK;
+        Long size = disk.getCapacityInBytes();
+        if (isImport) {
+            for (Backup.VolumeInfo volumeInfo : backedUpVolumes) {
+                if (volumeInfo.getSize().equals(disk.getCapacityInBytes())) {
+                    type = volumeInfo.getType();
+                }
+            }
+        }
+        VirtualDeviceBackingInfo backing = disk.getBacking();
+        checkBackingInfo(backing);
+        VirtualDiskFlatVer2BackingInfo info = (VirtualDiskFlatVer2BackingInfo) backing;
+        String volumeName = getVolumeName(disk, vmToImport);
+        Storage.ProvisioningType provisioningType = getProvisioningType(info);
+        long diskOfferingId = getDiskOfferingId(size, provisioningType);
+        Integer unitNumber = disk.getUnitNumber();
+        VirtualMachineDiskInfo diskInfo = getDiskInfo(vmToImport, poolId, volumeName);
+        return createVolumeRecord(type, volumeName, zoneId, domainId, accountId, diskOfferingId,
+                provisioningType, size, instanceId, poolId, templateId, unitNumber, diskInfo);
+    }
+
+    /**
+     * Get physical network ID from zoneId and Vmware label
+     */
+    private long getPhysicalNetworkId(Long zoneId, String tag) {
+        List<PhysicalNetworkVO> physicalNetworks = physicalNetworkDao.listByZone(zoneId);
+        for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
+            PhysicalNetworkTrafficTypeVO vo = _physicalNetworkTrafficTypeDao.findBy(physicalNetwork.getId(), TrafficType.Guest);
+            if (vo == null) {
+                continue;
+            }
+            String vmwareNetworkLabel = vo.getVmwareNetworkLabel();
+            if (!vmwareNetworkLabel.startsWith(tag)) {
+                throw new CloudRuntimeException("Vmware network label does not start with: " + tag);
+            }
+            return physicalNetwork.getId();
+        }
+        throw new CloudRuntimeException("Could not find guest physical network matching tag: " + tag + " on zone " + zoneId);
+    }
+
+    /**
+     * Create and persist network
+     */
+    private NetworkVO createNetworkRecord(Long zoneId, String tag, String vlan, long accountId, long domainId) {
+        Long physicalNetworkId = getPhysicalNetworkId(zoneId, tag);
+        final long id = _networkDao.getNextInSequence(Long.class, "id");
+        NetworkVO networkVO = new NetworkVO(id, TrafficType.Guest, Networks.Mode.Dhcp, BroadcastDomainType.Vlan, 9L,
+                domainId, accountId, id, "Imported-network-" + id, "Imported-network-" + id, null, Network.GuestType.Isolated,
+                zoneId, physicalNetworkId, ControlledEntity.ACLType.Account, false, null, false);
+        networkVO.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlan));
+        networkVO.setGuruName("ExternalGuestNetworkGuru");
+        networkVO.setState(Network.State.Implemented);
+        return _networkDao.persist(networkVO);
+    }
+
+    /**
+     * Get network from VM network name
+     */
+    private NetworkVO getGuestNetworkFromNetworkMorName(String name, long accountId, Long zoneId, long domainId) {
+        String prefix = "cloud.guest.";
+        String nameWithoutPrefix = name.replace(prefix, "");
+        String[] parts = nameWithoutPrefix.split("\\.");
+        String vlan = parts[0];
+        String tag = parts[parts.length - 1];
+        String[] tagSplit = tag.split("-");
+        tag = tagSplit[tagSplit.length - 1];
+        NetworkVO networkVO = _networkDao.findByVlan(vlan);
+        if (networkVO == null) {
+            networkVO = createNetworkRecord(zoneId, tag, vlan, accountId, domainId);
+        }
+        return networkVO;
+    }
+
+    /**
+     * Get map between VM networks and its IDs on CloudStack
+     */
+    private Map<String, NetworkVO> getNetworksMapping(String[] vmNetworkNames, long accountId, long zoneId, long domainId) {
+        Map<String, NetworkVO> mapping = new HashMap<>();
+        for (String networkName : vmNetworkNames) {
+            NetworkVO networkVO = getGuestNetworkFromNetworkMorName(networkName, accountId, zoneId, domainId);
+            mapping.put(networkName, networkVO);
+        }
+        return mapping;
+    }
+
+    /**
+     * Get network MO from VM NIC
+     */
+    private NetworkMO getNetworkMO(VirtualE1000 nic, VmwareContext context) {
+        VirtualDeviceConnectInfo connectable = nic.getConnectable();
+        VirtualEthernetCardNetworkBackingInfo info = (VirtualEthernetCardNetworkBackingInfo) nic.getBacking();
+        ManagedObjectReference networkMor = info.getNetwork();
+        if (networkMor == null) {
+            throw new CloudRuntimeException("Could not find network for NIC on: " + nic.getMacAddress());
+        }
+        return new NetworkMO(context, networkMor);
+    }
+
+    private Pair<String, String> getNicMacAddressAndNetworkName(VirtualDevice nicDevice, VmwareContext context) throws Exception {
+        VirtualE1000 nic = (VirtualE1000) nicDevice;
+        String macAddress = nic.getMacAddress();
+        NetworkMO networkMO = getNetworkMO(nic, context);
+        String networkName = networkMO.getName();
+        return new Pair<>(macAddress, networkName);
+    }
+
+    private void syncVMNics(VirtualDevice[] nicDevices, DatacenterMO dcMo, Map<String, NetworkVO> networksMapping,
+                            VMInstanceVO vm) throws Exception {
+        VmwareContext context = dcMo.getContext();
+        List<NicVO> allNics = _nicDao.listByVmId(vm.getId());
+        for (VirtualDevice nicDevice : nicDevices) {
+            Pair<String, String> pair = getNicMacAddressAndNetworkName(nicDevice, context);
+            String macAddress = pair.first();
+            String networkName = pair.second();
+            NetworkVO networkVO = networksMapping.get(networkName);
+            NicVO nicVO = _nicDao.findByNetworkIdAndMacAddress(networkVO.getId(), macAddress);
+            if (nicVO != null) {
+                allNics.remove(nicVO);
+            }
+        }
+        for (final NicVO unMappedNic : allNics) {
+            vmManager.removeNicFromVm(vm, unMappedNic);
+        }
+    }
+
+    private Map<VirtualDisk, VolumeVO> getDisksMapping(Backup backup, List<VirtualDisk> virtualDisks) {
+        final VMInstanceVO vm = _vmDao.findByIdIncludingRemoved(backup.getVmId());
+        if (vm == null) {
+            throw new CloudRuntimeException("Failed to find the volumes details from the VM backup");
+        }
+        List<Backup.VolumeInfo> backedUpVolumes = vm.getBackupVolumeList();
+        Map<String, Boolean> usedVols = new HashMap<>();
+        Map<VirtualDisk, VolumeVO> map = new HashMap<>();
+
+        for (Backup.VolumeInfo backedUpVol : backedUpVolumes) {
+            for (VirtualDisk disk : virtualDisks) {
+                if (!map.containsKey(disk) && backedUpVol.getSize().equals(disk.getCapacityInBytes())
+                        && !usedVols.containsKey(backedUpVol.getUuid())) {
+                    String volId = backedUpVol.getUuid();
+                    VolumeVO vol = _volumeDao.findByUuidIncludingRemoved(volId);
+                    usedVols.put(backedUpVol.getUuid(), true);
+                    map.put(disk, vol);
+                    s_logger.debug("VM restore mapping for disk " + disk.getBacking() +
+                            " (capacity: " + disk.getCapacityInBytes() + ") with volume ID" + vol.getId());
+                }
+            }
+        }
+        return map;
+    }
+
+    /**
+     * Find VM on datacenter
+     */
+    private VirtualMachineMO findVM(DatacenterMO dcMo, String path) throws Exception {
+        VirtualMachineMO vm = dcMo.findVm(path);
+        if (vm == null) {
+            throw new CloudRuntimeException("Error finding VM: " + path);
+        }
+        return vm;
+    }
+
+    /**
+     * Find restored volume based on volume info
+     */
+    private VirtualDisk findRestoredVolume(Backup.VolumeInfo volumeInfo, VirtualMachineMO vm) throws Exception {
+        List<VirtualDisk> virtualDisks = vm.getVirtualDisks();
+        for (VirtualDisk disk: virtualDisks) {
+            if (disk.getCapacityInBytes().equals(volumeInfo.getSize())) {
+                return disk;
+            }
+        }
+        throw new CloudRuntimeException("Volume to restore could not be found");
+    }
+
+    /**
+     * Get volume full path
+     */
+    private String getVolumeFullPath(VirtualDisk disk) {
+        VirtualDeviceBackingInfo backing = disk.getBacking();
+        checkBackingInfo(backing);
+        VirtualDiskFlatVer2BackingInfo info = (VirtualDiskFlatVer2BackingInfo) backing;
+        return info.getFileName();
+    }
+
+    /**
+     * Get dest volume full path
+     */
+    private String getDestVolumeFullPath(VirtualDisk restoredDisk, VirtualMachineMO restoredVm,
+                                         VirtualMachineMO vmMo) throws Exception {
+        VirtualDisk vmDisk = vmMo.getVirtualDisks().get(0);
+        String vmDiskPath = vmMo.getVmdkFileBaseName(vmDisk);
+        String vmDiskFullPath = getVolumeFullPath(vmMo.getVirtualDisks().get(0));
+        String restoredVolumePath = restoredVm.getVmdkFileBaseName(restoredDisk);
+        return vmDiskFullPath.replace(vmDiskPath, restoredVolumePath);
+    }
+
+    /**
+     * Get dest datastore mor
+     */
+    private ManagedObjectReference getDestStoreMor(VirtualMachineMO vmMo) throws Exception {
+        VirtualDisk vmDisk = vmMo.getVirtualDisks().get(0);
+        VirtualDeviceBackingInfo backing = vmDisk.getBacking();
+        checkBackingInfo(backing);
+        VirtualDiskFlatVer2BackingInfo info = (VirtualDiskFlatVer2BackingInfo) backing;
+        return info.getDatastore();
+    }
+
+    @Override
+    public VirtualMachine importVirtualMachineFromBackup(long zoneId, long domainId, long accountId, long userId,
+                                                         String vmInternalName, Backup backup) throws Exception {
+        DatacenterMO dcMo = getDatacenterMO(zoneId);
+        VirtualMachineMO vmToImport = dcMo.findVm(vmInternalName);
+        if (vmToImport == null) {
+            throw new CloudRuntimeException("Error finding VM: " + vmInternalName);
+        }
+        VirtualMachineConfigSummary configSummary = vmToImport.getConfigSummary();
+        VirtualMachineRuntimeInfo runtimeInfo = vmToImport.getRuntimeInfo();
+        List<VirtualDisk> virtualDisks = vmToImport.getVirtualDisks();
+        String[] vmNetworkNames = vmToImport.getNetworks();
+        VirtualDevice[] nicDevices = vmToImport.getNicDevices();
+
+        Map<VirtualDisk, VolumeVO> disksMapping = getDisksMapping(backup, virtualDisks);
+        Map<String, NetworkVO> networksMapping = getNetworksMapping(vmNetworkNames, accountId, zoneId, domainId);
+
+        long guestOsId = getImportingVMGuestOs(configSummary);
+        long serviceOfferingId = getImportingVMServiceOffering(configSummary, runtimeInfo);
+        long templateId = getImportingVMTemplate(virtualDisks, dcMo, vmInternalName, guestOsId, accountId, disksMapping, backup);
+
+        VMInstanceVO vm = getVM(vmInternalName, templateId, guestOsId, serviceOfferingId, zoneId, accountId, userId, domainId);
+        syncVMVolumes(vm, virtualDisks, disksMapping, vmToImport, backup);
+        syncVMNics(nicDevices, dcMo, networksMapping, vm);
+
+        return vm;
+    }
+
+    @Override
+    public boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backup.VolumeInfo volumeInfo,
+                                                        VirtualMachine vm, long poolId, Backup backup) throws Exception {
+        DatacenterMO dcMo = getDatacenterMO(zoneId);
+        VirtualMachineMO vmRestored = findVM(dcMo, location);
+        VirtualMachineMO vmMo = findVM(dcMo, vm.getInstanceName());
+        VirtualDisk restoredDisk = findRestoredVolume(volumeInfo, vmRestored);
+        String diskPath = vmRestored.getVmdkFileBaseName(restoredDisk);
+
+        s_logger.debug("Restored disk size=" + restoredDisk.getCapacityInKB() + " path=" + diskPath);
+
+        // Detach restored VM disks
+        vmRestored.detachAllDisks();
+
+        String srcPath = getVolumeFullPath(restoredDisk);
+        String destPath = getDestVolumeFullPath(restoredDisk, vmRestored, vmMo);
+
+        VirtualDiskManagerMO virtualDiskManagerMO = new VirtualDiskManagerMO(dcMo.getContext());
+
+        // Copy volume to the VM folder
+        virtualDiskManagerMO.moveVirtualDisk(srcPath, dcMo.getMor(), destPath, dcMo.getMor(), true);
+
+        try {
+            // Attach volume to VM
+            vmMo.attachDisk(new String[] {destPath}, getDestStoreMor(vmMo));
+        } catch (Exception e) {
+            s_logger.error("Failed to attach the restored volume: " + diskPath, e);
+            return false;
+        } finally {
+            // Destroy restored VM
+            vmRestored.destroy();
+        }
+
+        VirtualDisk attachedDisk = getAttachedDisk(vmMo, diskPath);
+        if (attachedDisk == null) {
+            s_logger.error("Failed to get the attached the (restored) volume " + diskPath);
+            return false;
+        }
+        createVolume(attachedDisk, vmMo, vm.getDomainId(), vm.getDataCenterId(), vm.getAccountId(), vm.getId(),
+                poolId, vm.getTemplateId(), backup, false);
+
+        return true;
+    }
+
+    private VirtualDisk getAttachedDisk(VirtualMachineMO vmMo, String diskPath) throws Exception {
+        for (VirtualDisk disk : vmMo.getVirtualDisks()) {
+            if (vmMo.getVmdkFileBaseName(disk).equals(diskPath)) {
+                return disk;
+            }
+        }
+        return null;
+    }
+
     @Override
     public List<Command> finalizeMigrate(VirtualMachine vm, StoragePool destination) {
         List<Command> commands = new ArrayList<Command>();
diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java
index 1d3c1ad..c4b939a 100644
--- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java
+++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java
@@ -199,7 +199,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
     @Inject
     private PrimaryDataStoreDao primaryStorageDao;
     @Inject
-    private VMTemplatePoolDao templateDataStoreDao;
+    private VMTemplatePoolDao templateStoragePoolDao;
     @Inject
     private TemplateJoinDao templateDao;
     @Inject
@@ -1443,7 +1443,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
      */
     private Runnable getCleanupFullyClonedTemplatesTask() {
         return new CleanupFullyClonedTemplatesTask(primaryStorageDao,
-                templateDataStoreDao,
+                templateStoragePoolDao,
                 templateDao,
                 vmInstanceDao,
                 cloneSettingDao,
diff --git a/plugins/pom.xml b/plugins/pom.xml
index 4a96b4d..2ecd697 100755
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -58,6 +58,8 @@
         <module>api/rate-limit</module>
         <module>api/solidfire-intg-test</module>
 
+        <module>backup/dummy</module>
+
         <module>ca/root-ca</module>
 
         <module>database/quota</module>
@@ -187,22 +189,13 @@
                 </property>
             </activation>
             <modules>
+                <module>api/vmware-sioc</module>
+                <module>backup/veeam</module>
                 <module>hypervisors/vmware</module>
                 <module>network-elements/cisco-vnmc</module>
             </modules>
         </profile>
         <profile>
-            <id>vmware-sioc</id>
-            <activation>
-                <property>
-                    <name>noredist</name>
-                </property>
-            </activation>
-            <modules>
-                <module>api/vmware-sioc</module>
-            </modules>
-        </profile>
-        <profile>
             <id>mysqlha</id>
             <activation>
                 <property>
diff --git a/pom.xml b/pom.xml
index 075b956..32d925f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -121,7 +121,7 @@
         <cs.bcprov.version>1.64</cs.bcprov.version>
         <cs.cglib.version>3.3.0</cs.cglib.version>
         <cs.checkstyle-lib.version>8.18</cs.checkstyle-lib.version>
-        <cs.cxf.version>3.2.0</cs.cxf.version>
+        <cs.cxf.version>3.2.6</cs.cxf.version>
         <cs.ehcache.version>2.6.11</cs.ehcache.version>
         <cs.globodns-client.version>0.0.27</cs.globodns-client.version>
         <cs.groovy.version>2.4.17</cs.groovy.version>
@@ -130,7 +130,7 @@
         <cs.httpclient.version>4.5.11</cs.httpclient.version>
         <cs.httpcore.version>4.4.13</cs.httpcore.version>
         <cs.influxdb-java.version>2.17</cs.influxdb-java.version>
-        <cs.jackson.version>2.9.2</cs.jackson.version>
+        <cs.jackson.version>2.9.6</cs.jackson.version>
         <cs.jasypt.version>1.9.3</cs.jasypt.version>
         <cs.java-ipv6.version>0.17</cs.java-ipv6.version>
         <cs.javassist.version>3.26.0-GA</cs.javassist.version>
@@ -161,6 +161,7 @@
         <cs.tomcat-embed-core.version>8.5.47</cs.tomcat-embed-core.version>
         <cs.trilead.version>1.0.0-build222</cs.trilead.version>
         <cs.vmware.api.version>6.7</cs.vmware.api.version>
+        <cs.winrm4j.version>0.5.0</cs.winrm4j.version>
         <cs.xapi.version>6.2.0-3.1</cs.xapi.version>
         <cs.xmlrpc.version>3.1.3</cs.xmlrpc.version>
         <cs.xstream.version>1.4.11.1</cs.xstream.version>
diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java
index 7457981..8d476ba 100644
--- a/server/src/main/java/com/cloud/api/ApiDBUtils.java
+++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java
@@ -39,6 +39,9 @@ import org.apache.cloudstack.api.ApiConstants.VMDetails;
 import org.apache.cloudstack.api.ResponseObject.ResponseView;
 import org.apache.cloudstack.api.response.AccountResponse;
 import org.apache.cloudstack.api.response.AsyncJobResponse;
+import org.apache.cloudstack.api.response.BackupOfferingResponse;
+import org.apache.cloudstack.api.response.BackupResponse;
+import org.apache.cloudstack.api.response.BackupScheduleResponse;
 import org.apache.cloudstack.api.response.DiskOfferingResponse;
 import org.apache.cloudstack.api.response.DomainResponse;
 import org.apache.cloudstack.api.response.DomainRouterResponse;
@@ -63,6 +66,12 @@ import org.apache.cloudstack.api.response.UserVmResponse;
 import org.apache.cloudstack.api.response.VolumeResponse;
 import org.apache.cloudstack.api.response.VpcOfferingResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
+import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.backup.BackupSchedule;
+import org.apache.cloudstack.backup.dao.BackupDao;
+import org.apache.cloudstack.backup.dao.BackupOfferingDao;
+import org.apache.cloudstack.backup.dao.BackupScheduleDao;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
@@ -446,6 +455,9 @@ public class ApiDBUtils {
     static ResourceMetaDataService s_resourceDetailsService;
     static HostGpuGroupsDao s_hostGpuGroupsDao;
     static VGPUTypesDao s_vgpuTypesDao;
+    static BackupDao s_backupDao;
+    static BackupScheduleDao s_backupScheduleDao;
+    static BackupOfferingDao s_backupOfferingDao;
 
     @Inject
     private ManagementServer ms;
@@ -684,6 +696,12 @@ public class ApiDBUtils {
     private HostGpuGroupsDao hostGpuGroupsDao;
     @Inject
     private VGPUTypesDao vgpuTypesDao;
+    @Inject
+    private BackupDao backupDao;
+    @Inject
+    private BackupOfferingDao backupOfferingDao;
+    @Inject
+    private BackupScheduleDao backupScheduleDao;
 
     @PostConstruct
     void init() {
@@ -806,6 +824,9 @@ public class ApiDBUtils {
         s_resourceDetailsService = resourceDetailsService;
         s_hostGpuGroupsDao = hostGpuGroupsDao;
         s_vgpuTypesDao = vgpuTypesDao;
+        s_backupDao = backupDao;
+        s_backupScheduleDao = backupScheduleDao;
+        s_backupOfferingDao = backupOfferingDao;
     }
 
     // ///////////////////////////////////////////////////////////
@@ -2037,4 +2058,16 @@ public class ApiDBUtils {
     public static List<ResourceTagJoinVO> listResourceTagViewByResourceUUID(String resourceUUID, ResourceObjectType resourceType) {
         return s_tagJoinDao.listBy(resourceUUID, resourceType);
     }
+
+    public static BackupResponse newBackupResponse(Backup backup) {
+        return s_backupDao.newBackupResponse(backup);
+    }
+
+    public static BackupScheduleResponse newBackupScheduleResponse(BackupSchedule schedule) {
+        return s_backupScheduleDao.newBackupScheduleResponse(schedule);
+    }
+
+    public static BackupOfferingResponse newBackupOfferingResponse(BackupOffering policy) {
+        return s_backupOfferingDao.newBackupOfferingResponse(policy);
+    }
 }
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index c674407..9da3ae4 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -49,6 +49,9 @@ import org.apache.cloudstack.api.response.AsyncJobResponse;
 import org.apache.cloudstack.api.response.AutoScalePolicyResponse;
 import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse;
 import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse;
+import org.apache.cloudstack.api.response.BackupOfferingResponse;
+import org.apache.cloudstack.api.response.BackupResponse;
+import org.apache.cloudstack.api.response.BackupScheduleResponse;
 import org.apache.cloudstack.api.response.CapabilityResponse;
 import org.apache.cloudstack.api.response.CapacityResponse;
 import org.apache.cloudstack.api.response.ClusterResponse;
@@ -141,6 +144,10 @@ import org.apache.cloudstack.api.response.VpcOfferingResponse;
 import org.apache.cloudstack.api.response.VpcResponse;
 import org.apache.cloudstack.api.response.VpnUsersResponse;
 import org.apache.cloudstack.api.response.ZoneResponse;
+import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.backup.BackupOffering;
+import org.apache.cloudstack.backup.BackupSchedule;
+import org.apache.cloudstack.backup.dao.BackupOfferingDao;
 import org.apache.cloudstack.config.Configuration;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
@@ -378,6 +385,8 @@ public class ApiResponseHelper implements ResponseGenerator {
     NetworkDetailsDao networkDetailsDao;
     @Inject
     private VMSnapshotDao vmSnapshotDao;
+    @Inject
+    private BackupOfferingDao backupOfferingDao;
 
     @Override
     public UserResponse createUserResponse(User user) {
@@ -3660,6 +3669,25 @@ public class ApiResponseHelper implements ResponseGenerator {
                 }
                 usageRecResponse.setDescription(builder.toString());
             }
+        } else if (usageRecord.getUsageType() == UsageTypes.BACKUP) {
+            resourceType = ResourceObjectType.Backup;
+            final StringBuilder builder = new StringBuilder();
+            builder.append("Backup usage of size ").append(usageRecord.getUsageDisplay());
+            if (vmInstance != null) {
+                resourceId = vmInstance.getId();
+                usageRecResponse.setResourceName(vmInstance.getInstanceName());
+                usageRecResponse.setUsageId(vmInstance.getUuid());
+                builder.append(" for VM ").append(vmInstance.getHostName())
+                        .append(" (").append(vmInstance.getUuid()).append(")");
+                final BackupOffering backupOffering = backupOfferingDao.findByIdIncludingRemoved(usageRecord.getOfferingId());
+                if (backupOffering != null) {
+                    builder.append(" and backup offering ").append(backupOffering.getName())
+                            .append(" (").append(backupOffering.getUuid()).append(", user ad-hoc/scheduled backup allowed: ")
+                            .append(backupOffering.isUserDrivenBackupAllowed()).append(")");
+                }
+
+            }
+            usageRecResponse.setDescription(builder.toString());
         } else if (usageRecord.getUsageType() == UsageTypes.VM_SNAPSHOT) {
             resourceType = ResourceObjectType.VMSnapshot;
             VMSnapshotVO vmSnapshotVO = null;
@@ -3672,6 +3700,9 @@ public class ApiResponseHelper implements ResponseGenerator {
                 }
             }
             usageRecResponse.setSize(usageRecord.getSize());
+            if (usageRecord.getVirtualSize() != null) {
+                usageRecResponse.setVirtualSize(usageRecord.getVirtualSize());
+            }
             if (usageRecord.getOfferingId() != null) {
                 usageRecResponse.setOfferingId(usageRecord.getOfferingId().toString());
             }
@@ -4210,6 +4241,22 @@ public class ApiResponseHelper implements ResponseGenerator {
         response.setDomainName(domain.getName());
         return response;
     }
+
+    @Override
+    public BackupResponse createBackupResponse(Backup backup) {
+        return ApiDBUtils.newBackupResponse(backup);
+    }
+
+    @Override
+    public BackupScheduleResponse createBackupScheduleResponse(BackupSchedule schedule) {
+        return ApiDBUtils.newBackupScheduleResponse(schedule);
+    }
+
+    @Override
+    public BackupOfferingResponse createBackupOfferingResponse(BackupOffering policy) {
+        return ApiDBUtils.newBackupOfferingResponse(policy);
+    }
+
     public ManagementServerResponse createManagementResponse(ManagementServerHost mgmt) {
         ManagementServerResponse response = new ManagementServerResponse();
         response.setId(mgmt.getUuid());
diff --git a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java
index 4ccfce9..a612829 100644
--- a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java
@@ -28,7 +28,6 @@ import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 
-import com.cloud.vm.UserVmManager;
 import org.apache.cloudstack.affinity.AffinityGroupResponse;
 import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.api.ApiConstants.VMDetails;
@@ -57,6 +56,7 @@ import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.net.Dhcp;
 import com.cloud.vm.UserVmDetailVO;
+import com.cloud.vm.UserVmManager;
 import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.VmStats;
 import com.cloud.vm.dao.NicExtraDhcpOptionDao;
@@ -174,6 +174,10 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
             userVmResponse.setDiskOfferingId(userVm.getDiskOfferingUuid());
             userVmResponse.setDiskOfferingName(userVm.getDiskOfferingName());
         }
+        if (details.contains(VMDetails.all) || details.contains(VMDetails.backoff)) {
+            userVmResponse.setBackupOfferingId(userVm.getBackupOfferingUuid());
+            userVmResponse.setBackupOfferingName(userVm.getBackupOfferingName());
+        }
         if (details.contains(VMDetails.all) || details.contains(VMDetails.servoff) || details.contains(VMDetails.stats)) {
             userVmResponse.setCpuNumber(userVm.getCpu());
             userVmResponse.setCpuSpeed(userVm.getSpeed());
diff --git a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java
index 7ff557c..6d48bec 100644
--- a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java
+++ b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java
@@ -211,6 +211,15 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
     @Column(name = "service_offering_name")
     private String serviceOfferingName;
 
+    @Column(name = "backup_offering_id")
+    private Long backupOfferingId;
+
+    @Column(name = "backup_offering_uuid")
+    private String backupOfferingUuid;
+
+    @Column(name = "backup_offering_name")
+    private String backupOfferingName;
+
     @Column(name = "cpu")
     private int cpu;
 
@@ -599,6 +608,14 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro
         return serviceOfferingName;
     }
 
+    public String getBackupOfferingUuid() {
+        return backupOfferingUuid;
+    }
+
+    public String getBackupOfferingName() {
+        return backupOfferingName;
+    }
+
     public int getCpu() {
         return cpu;
     }
diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
index d26b5ee..9e55506 100644
--- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
+++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
@@ -25,6 +25,7 @@ import javax.inject.Inject;
 import com.cloud.network.dao.NetworkDetailVO;
 import com.cloud.network.dao.NetworkDetailsDao;
 import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.backup.Backup;
 import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.log4j.Logger;
@@ -268,6 +269,17 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
     }
 
     @Override
+    public VirtualMachine importVirtualMachineFromBackup(long zoneId, long domainId, long accountId, long userId,
+                                                         String vmInternalName, Backup backup) throws Exception {
+        return null;
+    }
+
+    @Override
+    public boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backup.VolumeInfo volumeInfo,
+                                                        VirtualMachine vm, long poolId, Backup backup) throws Exception {
+        return false;
+    }
+
     public List<Command> finalizeMigrate(VirtualMachine vm, StoragePool destination) {
         return null;
     }
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index 15e3220..88af6e9 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -1637,6 +1637,11 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
             throw new InvalidParameterValueException("Unable to attach volume, please specify a VM that does not have VM snapshots");
         }
 
+        // if target VM has backups
+        if (vm.getBackupOfferingId() != null || vm.getBackupVolumeList().size() > 0) {
+            throw new InvalidParameterValueException("Unable to attach volume, please specify a VM that does not have any backups");
+        }
+
         // permission check
         _accountMgr.checkAccess(caller, null, true, volumeToAttach, vm);
 
@@ -1877,6 +1882,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
             throw new InvalidParameterValueException("Unable to detach volume, please specify a VM that does not have VM snapshots");
         }
 
+        if (vm.getBackupOfferingId() != null || vm.getBackupVolumeList().size() > 0) {
+            throw new InvalidParameterValueException("Unable to detach volume, cannot detach volume from a VM that has backups. First remove the VM from the backup offering.");
+        }
+
         AsyncJobExecutionContext asyncExecutionContext = AsyncJobExecutionContext.getCurrentExecutionContext();
         if (asyncExecutionContext != null) {
             AsyncJob job = asyncExecutionContext.getJob();
diff --git a/server/src/main/java/com/cloud/usage/UsageServiceImpl.java b/server/src/main/java/com/cloud/usage/UsageServiceImpl.java
index 531b291..4f09d13 100644
--- a/server/src/main/java/com/cloud/usage/UsageServiceImpl.java
+++ b/server/src/main/java/com/cloud/usage/UsageServiceImpl.java
@@ -16,6 +16,28 @@
 // under the License.
 package com.cloud.usage;
 
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
... 3305 lines suppressed ...