Skip to content

Commit 051d52c

Browse files
committed
Handle backup listing and restore for expunged and purged Instances
1 parent 3d3f6c1 commit 051d52c

File tree

15 files changed

+122
-51
lines changed

15 files changed

+122
-51
lines changed

api/src/main/java/com/cloud/vm/VirtualMachine.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ public static StateMachine2<State, VirtualMachine.Event, VirtualMachine> getStat
128128
s_fsm.addTransition(new Transition<State, Event>(State.Error, VirtualMachine.Event.DestroyRequested, State.Expunging, null));
129129
s_fsm.addTransition(new Transition<State, Event>(State.Error, VirtualMachine.Event.ExpungeOperation, State.Expunging, null));
130130
s_fsm.addTransition(new Transition<State, Event>(State.Stopped, Event.RestoringRequested, State.Restoring, null));
131-
s_fsm.addTransition(new Transition<State, Event>(State.Expunging, Event.RestoringRequested, State.Restoring, null));
132131
s_fsm.addTransition(new Transition<State, Event>(State.Destroyed, Event.RestoringRequested, State.Restoring, null));
133132
s_fsm.addTransition(new Transition<State, Event>(State.Restoring, Event.RestoringSuccess, State.Stopped, null));
134133
s_fsm.addTransition(new Transition<State, Event>(State.Restoring, Event.RestoringFailed, State.Stopped, null));

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ public class ApiConstants {
368368
public static final String OP = "op";
369369
public static final String OPTION = "option";
370370
public static final String OPTIONS = "options";
371+
public static final String IS_ORPHAN = "isorphan";
371372
public static final String OS_CATEGORY_ID = "oscategoryid";
372373
public static final String OS_CATEGORY_NAME = "oscategoryname";
373374
public static final String OS_NAME = "osname";

api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ public class BackupResponse extends BaseResponse {
119119
@Param(description = "the interval type of the backup schedule")
120120
private String intervalType;
121121

122+
@SerializedName(ApiConstants.IS_ORPHAN)
123+
@Param(description = "The backups is orphaned and no associated with an existing VM. A new VM can still be created using the backup")
124+
private Boolean isOrphan;
125+
122126
public String getId() {
123127
return id;
124128
}
@@ -294,4 +298,12 @@ public String getIntervalType() {
294298
public void setIntervalType(String intervalType) {
295299
this.intervalType = intervalType;
296300
}
301+
302+
public Boolean getIsOrphan() {
303+
return isOrphan;
304+
}
305+
306+
public void setIsOrphan(Boolean isOrphan) {
307+
this.isOrphan = isOrphan;
308+
}
297309
}

api/src/main/java/org/apache/cloudstack/backup/Backup.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,10 @@ public String toString() {
157157
}
158158
}
159159

160-
long getVmId();
160+
Long getVmId();
161+
String getVmName();
161162
long getBackupOfferingId();
163+
void setVmName(String vmName);
162164
String getExternalId();
163165
String getType();
164166
Date getDate();
@@ -168,7 +170,7 @@ public String toString() {
168170
void setName(String name);
169171
String getDescription();
170172
void setDescription(String description);
171-
short getBackupIntervalType();
173+
Short getBackupIntervalType();
172174
List<VolumeInfo> getBackedUpVolumes();
173175
long getZoneId();
174176
Map<String, String> getDetails();

api/src/main/java/org/apache/cloudstack/backup/BackupManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,5 +254,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
254254

255255
Map<String, String> getDiskOfferingDetailsForBackup(Long vmId);
256256

257+
void updateOrphanedBackups(VirtualMachine vm);
258+
257259
Capacity getBackupStorageUsedStats(Long zoneId);
258260
}

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd;
5858
import org.apache.cloudstack.api.command.admin.volume.MigrateVolumeCmdByAdmin;
5959
import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
60+
import org.apache.cloudstack.backup.BackupManager;
6061
import org.apache.cloudstack.ca.CAManager;
6162
import org.apache.cloudstack.context.CallContext;
6263
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@@ -409,6 +410,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
409410
ResourceCleanupService resourceCleanupService;
410411
@Inject
411412
VmWorkJobDao vmWorkJobDao;
413+
@Inject
414+
BackupManager backupManager;
412415

413416
private SingleCache<List<Long>> vmIdsInProgressCache;
414417

@@ -650,6 +653,8 @@ protected void advanceExpunge(VMInstanceVO vm) throws ResourceUnavailableExcepti
650653
throw new CloudRuntimeException("Unable to expunge " + vm, e);
651654
}
652655

656+
backupManager.updateOrphanedBackups(vm);
657+
653658
logger.debug("Expunging vm " + vm);
654659

655660
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);

engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ public class BackupVO implements Backup {
6060
private String uuid;
6161

6262
@Column(name = "vm_id")
63-
private long vmId;
63+
private Long vmId;
64+
65+
@Column(name = "vm_name")
66+
private String vmName;
6467

6568
@Column(name = "external_id")
6669
private String externalId;
@@ -98,7 +101,7 @@ public class BackupVO implements Backup {
98101
private long zoneId;
99102

100103
@Column(name = "backup_interval_type")
101-
private short backupIntervalType;
104+
private Short backupIntervalType;
102105

103106
@Column(name = "backed_volumes", length = 65535)
104107
protected String backedUpVolumes;
@@ -113,7 +116,7 @@ public BackupVO() {
113116
@Override
114117
public String toString() {
115118
return String.format("Backup %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
116-
this, "id", "uuid", "vmId", "backupType", "externalId"));
119+
this, "id", "uuid", "vmId", "vmName", "backupType", "externalId"));
117120
}
118121

119122
@Override
@@ -127,14 +130,24 @@ public String getUuid() {
127130
}
128131

129132
@Override
130-
public long getVmId() {
133+
public Long getVmId() {
131134
return vmId;
132135
}
133136

134-
public void setVmId(long vmId) {
137+
public void setVmId(Long vmId) {
135138
this.vmId = vmId;
136139
}
137140

141+
@Override
142+
public String getVmName() {
143+
return vmName;
144+
}
145+
146+
@Override
147+
public void setVmName(String vmName) {
148+
this.vmName = vmName;
149+
}
150+
138151
@Override
139152
public String getExternalId() {
140153
return externalId;
@@ -224,7 +237,7 @@ public void setZoneId(long zoneId) {
224237
}
225238

226239
@Override
227-
public short getBackupIntervalType() {
240+
public Short getBackupIntervalType() {
228241
return backupIntervalType;
229242
}
230243

engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -244,36 +244,39 @@ public void saveDetails(BackupVO backup) {
244244

245245
@Override
246246
public BackupResponse newBackupResponse(Backup backup, Boolean listVmDetails) {
247-
VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId());
248-
AccountVO account = accountDao.findByIdIncludingRemoved(vm.getAccountId());
249-
DomainVO domain = domainDao.findByIdIncludingRemoved(vm.getDomainId());
250-
DataCenterVO zone = dataCenterDao.findByIdIncludingRemoved(vm.getDataCenterId());
251-
Long offeringId = backup.getBackupOfferingId();
252-
if (offeringId == null) {
253-
offeringId = vm.getBackupOfferingId();
247+
VMInstanceVO vm = null;
248+
if (backup.getVmId() != null) {
249+
vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId());
254250
}
251+
AccountVO account = accountDao.findByIdIncludingRemoved(backup.getAccountId());
252+
DomainVO domain = domainDao.findByIdIncludingRemoved(backup.getDomainId());
253+
DataCenterVO zone = dataCenterDao.findByIdIncludingRemoved(backup.getZoneId());
254+
Long offeringId = backup.getBackupOfferingId();
255255
BackupOffering offering = backupOfferingDao.findByIdIncludingRemoved(offeringId);
256256

257257
BackupResponse response = new BackupResponse();
258258
response.setId(backup.getUuid());
259-
if (backup.getName() != null) {
260-
response.setName(backup.getName());
259+
response.setName(backup.getName());
260+
response.setDescription(backup.getDescription());
261+
if (vm != null) {
262+
response.setVmId(vm.getUuid());
263+
response.setVmName(vm.getHostName());
261264
} else {
262-
response.setName(vm.getHostName());
265+
response.setVmName(backup.getVmName());
266+
response.setIsOrphan(true);
263267
}
264-
response.setDescription(backup.getDescription());
265-
response.setVmId(vm.getUuid());
266-
response.setVmName(vm.getHostName());
267268
response.setExternalId(backup.getExternalId());
268269
response.setType(backup.getType());
269270
response.setDate(backup.getDate());
270271
response.setSize(backup.getSize());
271272
response.setProtectedSize(backup.getProtectedSize());
272273
response.setStatus(backup.getStatus());
273-
response.setIntervalType(Backup.Type.values()[backup.getBackupIntervalType()].toString());
274+
if (backup.getBackupIntervalType() != null) {
275+
response.setIntervalType(Backup.Type.values()[backup.getBackupIntervalType()].toString());
276+
}
274277
// ACS 4.20: For backups taken prior this release the backup.backed_volumes column would be empty hence use vm_instance.backup_volumes
275278
String backedUpVolumes;
276-
if (Objects.isNull(backup.getBackedUpVolumes())) {
279+
if (Objects.isNull(backup.getBackedUpVolumes()) && vm != null) {
277280
backedUpVolumes = new Gson().toJson(vm.getBackupVolumeList().toArray(), Backup.VolumeInfo[].class);
278281
} else {
279282
backedUpVolumes = new Gson().toJson(backup.getBackedUpVolumes().toArray(), Backup.VolumeInfo[].class);

engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,36 @@
1919
-- Schema upgrade from 4.20.1.0 to 4.21.0.0
2020
--;
2121

22+
-- Add console_endpoint_creator_address column to cloud.console_session table
23+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'console_endpoint_creator_address', 'VARCHAR(45)');
24+
25+
-- Add client_address column to cloud.console_session table
26+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'client_address', 'VARCHAR(45)');
27+
28+
-- Allow default roles to use quotaCreditsList
29+
INSERT INTO `cloud`.`role_permissions` (uuid, role_id, rule, permission, sort_order)
30+
SELECT uuid(), role_id, 'quotaCreditsList', permission, sort_order
31+
FROM `cloud`.`role_permissions` rp
32+
WHERE rp.rule = 'quotaStatement'
33+
AND NOT EXISTS(SELECT 1 FROM cloud.role_permissions rp_ WHERE rp.role_id = rp_.role_id AND rp_.rule = 'quotaCreditsList');
34+
35+
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host', 'last_mgmt_server_id', 'bigint unsigned DEFAULT NULL COMMENT "last management server this host is connected to" AFTER `mgmt_server_id`');
36+
2237
-- Add column max_backup to backup_schedule table
2338
ALTER TABLE `cloud`.`backup_schedule` ADD COLUMN `max_backups` int(8) default NULL COMMENT 'maximum number of backups to maintain';
2439

2540
-- Add columns name, description and backup_interval_type to backup table
2641
ALTER TABLE `cloud`.`backups` ADD COLUMN `backup_interval_type` int(5) COMMENT 'type of backup, e.g. manual, recurring - hourly, daily, weekly or monthly';
27-
ALTER TABLE `cloud`.`backups` ADD COLUMN `name` varchar(255) COMMENT 'name of the backup';
42+
ALTER TABLE `cloud`.`backups` ADD COLUMN `name` varchar(255) NOT NULL COMMENT 'name of the backup';
43+
ALTER TABLE `cloud`.`backups` ADD COLUMN `vm_name` varchar(255) COMMENT 'name of the vm for which backup is taken, only set for orphaned backups';
2844
ALTER TABLE `cloud`.`backups` ADD COLUMN `description` varchar(1024) COMMENT 'description for the backup';
45+
UPDATE `cloud`.`backups` JOIN `cloud`.`vm_instance` ON `backups`.`vm_id` = `vm_instance`.`id` SET `backups`.`name` = `vm_instance`.`name`;
2946

30-
-- Add console_endpoint_creator_address column to cloud.console_session table
31-
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'console_endpoint_creator_address', 'VARCHAR(45)');
32-
33-
-- Add client_address column to cloud.console_session table
34-
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'client_address', 'VARCHAR(45)');
47+
-- Make the column vm_id in backups table nullable and set it to null if foreign key is deleted
48+
ALTER TABLE `cloud`.`backups` DROP FOREIGN KEY `fk_backup__vm_id``;
49+
ALTER TABLE `cloud`.`backups` MODIFY COLUMN `vm_id` BIGINT UNSIGNED NULL;
50+
ALTER TABLE `cloud`.`backups` ADD CONSTRAINT `fk_backup__vm_id`` FOREIGN KEY (`vm_id``) REFERENCES `cloud`.`vm_instance`(`id`) ON DELETE SET NULL;
51+
ALTER TABLE `cloud`.`backups` varchar(255) NOT NULL COMMENT 'name of the backup'
3552
3653
-- Create backup details table
3754
CREATE TABLE `cloud`.`backup_details` (
@@ -44,15 +61,6 @@ CREATE TABLE `cloud`.`backup_details` (
4461
CONSTRAINT `fk_backup_details__backup_id` FOREIGN KEY `fk_backup_details__backup_id`(`backup_id`) REFERENCES `backups`(`id`) ON DELETE CASCADE
4562
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
4663
47-
-- Allow default roles to use quotaCreditsList
48-
INSERT INTO `cloud`.`role_permissions` (uuid, role_id, rule, permission, sort_order)
49-
SELECT uuid(), role_id, 'quotaCreditsList', permission, sort_order
50-
FROM `cloud`.`role_permissions` rp
51-
WHERE rp.rule = 'quotaStatement'
52-
AND NOT EXISTS(SELECT 1 FROM cloud.role_permissions rp_ WHERE rp.role_id = rp_.role_id AND rp_.rule = 'quotaCreditsList');
53-
54-
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host', 'last_mgmt_server_id', 'bigint unsigned DEFAULT NULL COMMENT "last management server this host is connected to" AFTER `mgmt_server_id`');
55-
5664
-- Add column allocated_size to object_store table. Rename column 'used_bytes' to 'used_size'
5765
ALTER TABLE `cloud`.`object_store` ADD COLUMN `allocated_size` bigint unsigned COMMENT 'allocated size in bytes';
5866
ALTER TABLE `cloud`.`object_store` CHANGE COLUMN `used_bytes` `used_size` BIGINT UNSIGNED COMMENT 'used size in bytes';

plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,13 @@ public boolean deleteBackup(Backup backup, boolean forced) {
370370
throw new CloudRuntimeException("No valid backup repository found for the VM, please check the attached backup offering");
371371
}
372372

373-
final VirtualMachine vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId());
374-
final Host host = getLastVMHypervisorHost(vm);
373+
final Host host;
374+
final VirtualMachine vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId());
375+
if (vm != null) {
376+
host = getLastVMHypervisorHost(vm);
377+
} else {
378+
host = getUpHostInZone(backup.getZoneId());
379+
}
375380

376381
DeleteBackupCommand command = new DeleteBackupCommand(backup.getExternalId(), backupRepository.getType(),
377382
backupRepository.getAddress(), backupRepository.getMountOptions());

0 commit comments

Comments
 (0)