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 ...