Skip to content

Commit c361409

Browse files
Merge branch '4.22'
2 parents 30dd234 + 03de62b commit c361409

File tree

8 files changed

+209
-52
lines changed

8 files changed

+209
-52
lines changed

core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public class RestoreBackupCommand extends Command {
3434
private List<String> backupVolumesUUIDs;
3535
private List<PrimaryDataStoreTO> restoreVolumePools;
3636
private List<String> restoreVolumePaths;
37+
private List<Long> restoreVolumeSizes;
3738
private List<String> backupFiles;
3839
private String diskType;
3940
private Boolean vmExists;
@@ -92,6 +93,14 @@ public void setRestoreVolumePaths(List<String> restoreVolumePaths) {
9293
this.restoreVolumePaths = restoreVolumePaths;
9394
}
9495

96+
public List<Long> getRestoreVolumeSizes() {
97+
return restoreVolumeSizes;
98+
}
99+
100+
public void setRestoreVolumeSizes(List<Long> restoreVolumeSizes) {
101+
this.restoreVolumeSizes = restoreVolumeSizes;
102+
}
103+
95104
public List<String> getBackupFiles() {
96105
return backupFiles;
97106
}

engine/schema/src/main/resources/META-INF/db/schema-42200to42210-cleanup.sql

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,3 @@
1818
--;
1919
-- Schema upgrade cleanup from 4.22.0.0 to 4.22.1.0
2020
--;
21-
22-
DROP VIEW IF EXISTS `cloud`.`account_netstats_view`;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
-- Licensed to the Apache Software Foundation (ASF) under one
2+
-- or more contributor license agreements. See the NOTICE file
3+
-- distributed with this work for additional information
4+
-- regarding copyright ownership. The ASF licenses this file
5+
-- to you under the Apache License, Version 2.0 (the
6+
-- "License"); you may not use this file except in compliance
7+
-- with the License. You may obtain a copy of the License at
8+
--
9+
-- http://www.apache.org/licenses/LICENSE-2.0
10+
--
11+
-- Unless required by applicable law or agreed to in writing,
12+
-- software distributed under the License is distributed on an
13+
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
-- KIND, either express or implied. See the License for the
15+
-- specific language governing permissions and limitations
16+
-- under the License.
17+
18+
-- cloud.account_netstats_view source
19+
20+
21+
DROP VIEW IF EXISTS `cloud`.`account_netstats_view`;
22+
23+
CREATE VIEW `cloud`.`account_netstats_view` AS
24+
select
25+
`user_statistics`.`account_id` AS `account_id`,
26+
(sum(`user_statistics`.`net_bytes_received`) + sum(`user_statistics`.`current_bytes_received`)) AS `bytesReceived`,
27+
(sum(`user_statistics`.`net_bytes_sent`) + sum(`user_statistics`.`current_bytes_sent`)) AS `bytesSent`
28+
from
29+
`user_statistics`
30+
group by
31+
`user_statistics`.`account_id`;

engine/schema/src/main/resources/META-INF/db/views/cloud.account_view.sql

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ select
3939
`data_center`.`id` AS `data_center_id`,
4040
`data_center`.`uuid` AS `data_center_uuid`,
4141
`data_center`.`name` AS `data_center_name`,
42-
`account_netstats`.`bytesReceived` AS `bytesReceived`,
43-
`account_netstats`.`bytesSent` AS `bytesSent`,
42+
`account_netstats_view`.`bytesReceived` AS `bytesReceived`,
43+
`account_netstats_view`.`bytesSent` AS `bytesSent`,
4444
`vmlimit`.`max` AS `vmLimit`,
4545
`vmcount`.`count` AS `vmTotal`,
4646
`runningvm`.`vmcount` AS `runningVms`,
@@ -89,15 +89,8 @@ from
8989
`cloud`.`domain` ON account.domain_id = domain.id
9090
left join
9191
`cloud`.`data_center` ON account.default_zone_id = data_center.id
92-
left join lateral (
93-
select
94-
coalesce(sum(`user_statistics`.`net_bytes_received` + `user_statistics`.`current_bytes_received`), 0) AS `bytesReceived`,
95-
coalesce(sum(`user_statistics`.`net_bytes_sent` + `user_statistics`.`current_bytes_sent`), 0) AS `bytesSent`
96-
from
97-
`cloud`.`user_statistics`
98-
where
99-
`user_statistics`.`account_id` = `account`.`id`
100-
) AS `account_netstats` ON TRUE
92+
left join
93+
`cloud`.`account_netstats_view` ON account.id = account_netstats_view.account_id
10194
left join
10295
`cloud`.`resource_limit` vmlimit ON account.id = vmlimit.account_id
10396
and vmlimit.type = 'user_vm' and vmlimit.tag IS NULL

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ private Pair<List<PrimaryDataStoreTO>, List<String>> getVolumePoolsAndPaths(List
357357
volumePools.add(dataStore != null ? (PrimaryDataStoreTO)dataStore.getTO() : null);
358358

359359
String volumePathPrefix = getVolumePathPrefix(storagePool);
360-
volumePaths.add(String.format("%s/%s", volumePathPrefix, volume.getPath()));
360+
String volumePathSuffix = getVolumePathSuffix(storagePool);
361+
volumePaths.add(String.format("%s%s%s", volumePathPrefix, volume.getPath(), volumePathSuffix));
361362
}
362363
return new Pair<>(volumePools, volumePaths);
363364
}
@@ -367,14 +368,24 @@ private String getVolumePathPrefix(StoragePoolVO storagePool) {
367368
if (ScopeType.HOST.equals(storagePool.getScope()) ||
368369
Storage.StoragePoolType.SharedMountPoint.equals(storagePool.getPoolType()) ||
369370
Storage.StoragePoolType.RBD.equals(storagePool.getPoolType())) {
370-
volumePathPrefix = storagePool.getPath();
371+
volumePathPrefix = storagePool.getPath() + "/";
372+
} else if (Storage.StoragePoolType.Linstor.equals(storagePool.getPoolType())) {
373+
volumePathPrefix = "/dev/drbd/by-res/cs-";
371374
} else {
372375
// Should be Storage.StoragePoolType.NetworkFilesystem
373-
volumePathPrefix = String.format("/mnt/%s", storagePool.getUuid());
376+
volumePathPrefix = String.format("/mnt/%s/", storagePool.getUuid());
374377
}
375378
return volumePathPrefix;
376379
}
377380

381+
private String getVolumePathSuffix(StoragePoolVO storagePool) {
382+
if (Storage.StoragePoolType.Linstor.equals(storagePool.getPoolType())) {
383+
return "/0";
384+
} else {
385+
return "";
386+
}
387+
}
388+
378389
@Override
379390
public Pair<Boolean, String> restoreBackedUpVolume(Backup backup, Backup.VolumeInfo backupVolumeInfo, String hostIp, String dataStoreUuid, Pair<String, VirtualMachine.State> vmNameAndState) {
380391
final VolumeVO volume = volumeDao.findByUuid(backupVolumeInfo.getUuid());
@@ -419,7 +430,9 @@ public Pair<Boolean, String> restoreBackedUpVolume(Backup backup, Backup.VolumeI
419430
restoreCommand.setBackupRepoType(backupRepository.getType());
420431
restoreCommand.setBackupRepoAddress(backupRepository.getAddress());
421432
restoreCommand.setVmName(vmNameAndState.first());
422-
restoreCommand.setRestoreVolumePaths(Collections.singletonList(String.format("%s/%s", getVolumePathPrefix(pool), volumeUUID)));
433+
String restoreVolumePath = String.format("%s%s%s", getVolumePathPrefix(pool), volumeUUID, getVolumePathSuffix(pool));
434+
restoreCommand.setRestoreVolumePaths(Collections.singletonList(restoreVolumePath));
435+
restoreCommand.setRestoreVolumeSizes(Collections.singletonList(backedUpVolumeSize));
423436
DataStore dataStore = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
424437
restoreCommand.setRestoreVolumePools(Collections.singletonList(dataStore != null ? (PrimaryDataStoreTO)dataStore.getTO() : null));
425438
restoreCommand.setDiskType(backupVolumeInfo.getType().name().toLowerCase(Locale.ROOT));

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@
4141
import org.apache.commons.lang3.StringUtils;
4242
import org.libvirt.LibvirtException;
4343

44-
import java.io.File;
4544
import java.io.IOException;
4645
import java.nio.file.Files;
46+
import java.nio.file.Path;
4747
import java.nio.file.Paths;
4848
import java.util.List;
4949
import java.util.Locale;
@@ -56,10 +56,25 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
5656
private static final String UMOUNT_COMMAND = "sudo umount %s";
5757
private static final String FILE_PATH_PLACEHOLDER = "%s/%s";
5858
private static final String ATTACH_QCOW2_DISK_COMMAND = " virsh attach-disk %s %s %s --driver qemu --subdriver qcow2 --cache none";
59+
private static final String ATTACH_RAW_DISK_COMMAND = " virsh attach-disk %s %s %s --driver qemu --cache none";
5960
private static final String ATTACH_RBD_DISK_XML_COMMAND = " virsh attach-device %s /dev/stdin <<EOF%sEOF";
6061
private static final String CURRRENT_DEVICE = "virsh domblklist --domain %s | tail -n 3 | head -n 1 | awk '{print $1}'";
6162
private static final String RSYNC_COMMAND = "rsync -az %s %s";
6263

64+
private String getVolumeUuidFromPath(String volumePath, PrimaryDataStoreTO volumePool) {
65+
if (Storage.StoragePoolType.Linstor.equals(volumePool.getPoolType())) {
66+
Path path = Paths.get(volumePath);
67+
String rscName = path.getParent().getFileName().toString();
68+
if (rscName.startsWith("cs-")) {
69+
rscName = rscName.substring(3);
70+
}
71+
return rscName;
72+
} else {
73+
int lastIndex = volumePath.lastIndexOf("/");
74+
return volumePath.substring(lastIndex + 1);
75+
}
76+
}
77+
6378
@Override
6479
public Answer execute(RestoreBackupCommand command, LibvirtComputingResource serverResource) {
6580
String vmName = command.getVmName();
@@ -84,9 +99,9 @@ public Answer execute(RestoreBackupCommand command, LibvirtComputingResource ser
8499
PrimaryDataStoreTO volumePool = restoreVolumePools.get(0);
85100
String volumePath = restoreVolumePaths.get(0);
86101
String backupFile = backupFiles.get(0);
87-
int lastIndex = volumePath.lastIndexOf("/");
88-
newVolumeId = volumePath.substring(lastIndex + 1);
89-
restoreVolume(storagePoolMgr, backupPath, volumePool, volumePath, diskType, backupFile,
102+
newVolumeId = getVolumeUuidFromPath(volumePath, volumePool);
103+
Long size = command.getRestoreVolumeSizes().get(0);
104+
restoreVolume(storagePoolMgr, backupPath, volumePool, volumePath, diskType, backupFile, size,
90105
new Pair<>(vmName, command.getVmState()), mountDirectory, timeout);
91106
} else if (Boolean.TRUE.equals(vmExists)) {
92107
restoreVolumesOfExistingVM(storagePoolMgr, restoreVolumePools, restoreVolumePaths, backedVolumeUUIDs, backupPath, backupFiles, mountDirectory, timeout);
@@ -143,7 +158,7 @@ private void restoreVolumesOfDestroyedVMs(KVMStoragePoolManager storagePoolMgr,
143158
String volumePath = volumePaths.get(i);
144159
String backupFile = backupFiles.get(i);
145160
String bkpPath = getBackupPath(mountDirectory, backupPath, backupFile, diskType);
146-
String volumeUuid = volumePath.substring(volumePath.lastIndexOf(File.separator) + 1);
161+
String volumeUuid = getVolumeUuidFromPath(volumePath, volumePool);
147162
diskType = "datadisk";
148163
verifyBackupFile(bkpPath, volumeUuid);
149164
if (!replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, bkpPath, timeout)) {
@@ -157,14 +172,14 @@ private void restoreVolumesOfDestroyedVMs(KVMStoragePoolManager storagePoolMgr,
157172
}
158173

159174
private void restoreVolume(KVMStoragePoolManager storagePoolMgr, String backupPath, PrimaryDataStoreTO volumePool, String volumePath, String diskType, String backupFile,
160-
Pair<String, VirtualMachine.State> vmNameAndState, String mountDirectory, int timeout) {
175+
Long size, Pair<String, VirtualMachine.State> vmNameAndState, String mountDirectory, int timeout) {
161176
String bkpPath;
162177
String volumeUuid;
163178
try {
164179
bkpPath = getBackupPath(mountDirectory, backupPath, backupFile, diskType);
165-
volumeUuid = volumePath.substring(volumePath.lastIndexOf(File.separator) + 1);
180+
volumeUuid = getVolumeUuidFromPath(volumePath, volumePool);
166181
verifyBackupFile(bkpPath, volumeUuid);
167-
if (!replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, bkpPath, timeout, true)) {
182+
if (!replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, bkpPath, timeout, true, size)) {
168183
throw new CloudRuntimeException(String.format("Unable to restore contents from the backup volume [%s].", volumeUuid));
169184

170185
}
@@ -247,42 +262,66 @@ private boolean checkBackupPathExists(String backupPath) {
247262
}
248263

249264
private boolean replaceVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout) {
250-
return replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, false);
265+
return replaceVolumeWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, false, null);
251266
}
252267

253-
private boolean replaceVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume) {
254-
if (volumePool.getPoolType() != Storage.StoragePoolType.RBD) {
255-
int exitValue = Script.runSimpleBashScriptForExitValue(String.format(RSYNC_COMMAND, backupPath, volumePath));
256-
return exitValue == 0;
268+
private boolean replaceVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume, Long size) {
269+
if (List.of(Storage.StoragePoolType.RBD, Storage.StoragePoolType.Linstor).contains(volumePool.getPoolType())) {
270+
return replaceBlockDeviceWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, createTargetVolume, size);
257271
}
258272

259-
return replaceRbdVolumeWithBackup(storagePoolMgr, volumePool, volumePath, backupPath, timeout, createTargetVolume);
273+
int exitValue = Script.runSimpleBashScriptForExitValue(String.format(RSYNC_COMMAND, backupPath, volumePath));
274+
return exitValue == 0;
260275
}
261276

262-
private boolean replaceRbdVolumeWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume) {
277+
private boolean replaceBlockDeviceWithBackup(KVMStoragePoolManager storagePoolMgr, PrimaryDataStoreTO volumePool, String volumePath, String backupPath, int timeout, boolean createTargetVolume, Long size) {
263278
KVMStoragePool volumeStoragePool = storagePoolMgr.getStoragePool(volumePool.getPoolType(), volumePool.getUuid());
264279
QemuImg qemu;
265280
try {
266281
qemu = new QemuImg(timeout * 1000, true, false);
267-
if (!createTargetVolume) {
268-
KVMPhysicalDisk rdbDisk = volumeStoragePool.getPhysicalDisk(volumePath);
269-
logger.debug("Restoring RBD volume: {}", rdbDisk.toString());
282+
String volumeUuid = getVolumeUuidFromPath(volumePath, volumePool);
283+
KVMPhysicalDisk disk = null;
284+
if (createTargetVolume) {
285+
if (Storage.StoragePoolType.Linstor.equals(volumePool.getPoolType())) {
286+
if (size == null) {
287+
throw new CloudRuntimeException("Restore volume size is required for Linstor pool when creating target volume");
288+
}
289+
disk = volumeStoragePool.createPhysicalDisk(volumeUuid, QemuImg.PhysicalDiskFormat.RAW, Storage.ProvisioningType.THIN, size, null);
290+
}
291+
} else {
292+
if (Storage.StoragePoolType.Linstor.equals(volumePool.getPoolType())) {
293+
storagePoolMgr.connectPhysicalDisk(volumePool.getPoolType(), volumePool.getUuid(), volumeUuid, null);
294+
} else {
295+
disk = volumeStoragePool.getPhysicalDisk(volumePath);
296+
}
270297
qemu.setSkipTargetVolumeCreation(true);
271298
}
299+
if (disk != null) {
300+
logger.debug("Restoring volume: {}", disk.toString());
301+
}
272302
} catch (LibvirtException ex) {
273-
throw new CloudRuntimeException("Failed to create qemu-img command to restore RBD volume with backup", ex);
303+
throw new CloudRuntimeException(String.format("Failed to create qemu-img command to restore %s volume with backup", volumePool.getPoolType()), ex);
274304
}
275305

276306
QemuImgFile srcBackupFile = null;
277307
QemuImgFile destVolumeFile = null;
278308
try {
279309
srcBackupFile = new QemuImgFile(backupPath, QemuImg.PhysicalDiskFormat.QCOW2);
280-
String rbdDestVolumeFile = KVMPhysicalDisk.RBDStringBuilder(volumeStoragePool, volumePath);
281-
destVolumeFile = new QemuImgFile(rbdDestVolumeFile, QemuImg.PhysicalDiskFormat.RAW);
282-
283-
logger.debug("Starting convert backup {} to RBD volume {}", backupPath, volumePath);
310+
String destVolume;
311+
switch(volumePool.getPoolType()) {
312+
case Linstor:
313+
destVolume = volumePath;
314+
break;
315+
case RBD:
316+
destVolume = KVMPhysicalDisk.RBDStringBuilder(volumeStoragePool, volumePath);
317+
break;
318+
default:
319+
throw new CloudRuntimeException(String.format("Unsupported storage pool type [%s] for block device restore with backup.", volumePool.getPoolType()));
320+
}
321+
destVolumeFile = new QemuImgFile(destVolume, QemuImg.PhysicalDiskFormat.RAW);
322+
logger.debug("Starting convert backup {} to volume {}", backupPath, volumePath);
284323
qemu.convert(srcBackupFile, destVolumeFile);
285-
logger.debug("Successfully converted backup {} to RBD volume {}", backupPath, volumePath);
324+
logger.debug("Successfully converted backup {} to volume {}", backupPath, volumePath);
286325
} catch (QemuImgException | LibvirtException e) {
287326
String srcFilename = srcBackupFile != null ? srcBackupFile.getFileName() : null;
288327
String destFilename = destVolumeFile != null ? destVolumeFile.getFileName() : null;
@@ -296,12 +335,14 @@ private boolean replaceRbdVolumeWithBackup(KVMStoragePoolManager storagePoolMgr,
296335
private boolean attachVolumeToVm(KVMStoragePoolManager storagePoolMgr, String vmName, PrimaryDataStoreTO volumePool, String volumePath) {
297336
String deviceToAttachDiskTo = getDeviceToAttachDisk(vmName);
298337
int exitValue;
299-
if (volumePool.getPoolType() != Storage.StoragePoolType.RBD) {
300-
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_QCOW2_DISK_COMMAND, vmName, volumePath, deviceToAttachDiskTo));
301-
} else {
338+
if (volumePool.getPoolType() == Storage.StoragePoolType.RBD) {
302339
String xmlForRbdDisk = getXmlForRbdDisk(storagePoolMgr, volumePool, volumePath, deviceToAttachDiskTo);
303340
logger.debug("RBD disk xml to attach: {}", xmlForRbdDisk);
304341
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_RBD_DISK_XML_COMMAND, vmName, xmlForRbdDisk));
342+
} else if (volumePool.getPoolType() == Storage.StoragePoolType.Linstor) {
343+
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_RAW_DISK_COMMAND, vmName, volumePath, deviceToAttachDiskTo));
344+
} else {
345+
exitValue = Script.runSimpleBashScriptForExitValue(String.format(ATTACH_QCOW2_DISK_COMMAND, vmName, volumePath, deviceToAttachDiskTo));
305346
}
306347
return exitValue == 0;
307348
}

0 commit comments

Comments
 (0)