diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java index 0090b4f6b16c..257dc1cfcd40 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -59,6 +59,12 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer "false", "Enable volume attach/detach operations for VMs that are assigned to Backup Offerings.", true); + ConfigKey NASBackupParallelExecution = new ConfigKey<>("Advanced", Boolean.class, + "backup.nas.parallel.execution.enabled", + "true", + "Allow NAS take-backup commands to run in parallel with other backup and delete commands on the KVM host. Disable to force sequential execution.", + true, ConfigKey.Scope.Zone); + /** * List backup provider offerings * @param zoneId zone id diff --git a/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java index 93855ea17211..f225c55bb46e 100644 --- a/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java +++ b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java @@ -32,6 +32,7 @@ public class TakeBackupCommand extends Command { private List volumePaths; @LogLevel(LogLevel.Log4jLevel.Off) private String mountOptions; + private boolean executeInSequence = false; public TakeBackupCommand(String vmName, String backupPath) { super(); @@ -89,6 +90,13 @@ public void setVolumePaths(List volumePaths) { @Override public boolean executeInSequence() { - return true; + // Parallel by default: each backup uses its own per-VM on-NAS path, so concurrent + // runs do not contend. Operators can force sequential execution per zone via the + // backup.nas.parallel.execution.enabled setting. + return executeInSequence; + } + + public void setExecuteInSequence(boolean executeInSequence) { + this.executeInSequence = executeInSequence; } } diff --git a/core/src/test/java/org/apache/cloudstack/backup/TakeBackupCommandTest.java b/core/src/test/java/org/apache/cloudstack/backup/TakeBackupCommandTest.java new file mode 100644 index 000000000000..0f86c2142bdb --- /dev/null +++ b/core/src/test/java/org/apache/cloudstack/backup/TakeBackupCommandTest.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; + +import org.junit.Assert; +import org.junit.Test; + +public class TakeBackupCommandTest { + + @Test + public void testExecuteInSequenceDefaultsToParallel() { + TakeBackupCommand command = new TakeBackupCommand("vm-1", "/backups/vm-1"); + // Default: run in parallel with other backup/delete commands. + Assert.assertFalse(command.executeInSequence()); + } + + @Test + public void testExecuteInSequenceIsSettable() { + TakeBackupCommand command = new TakeBackupCommand("vm-1", "/backups/vm-1"); + + command.setExecuteInSequence(true); + Assert.assertTrue(command.executeInSequence()); + + command.setExecuteInSequence(false); + Assert.assertFalse(command.executeInSequence()); + } +} diff --git a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java index 565ea29acf8b..33d71668ca9f 100644 --- a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java +++ b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java @@ -159,6 +159,7 @@ public boolean takeBackup(final VirtualMachine vm) { command.setBackupRepoType(backupRepository.getType()); command.setBackupRepoAddress(backupRepository.getAddress()); command.setMountOptions(backupRepository.getMountOptions()); + command.setExecuteInSequence(!BackupManager.NASBackupParallelExecution.valueIn(vm.getDataCenterId())); if (VirtualMachine.State.Stopped.equals(vm.getState())) { List vmVolumes = volumeDao.findByInstance(vm.getId()); diff --git a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java index b3b22e98e45b..2c6cc10b8fd2 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -1023,7 +1023,8 @@ public ConfigKey[] getConfigKeys() { BackupFrameworkEnabled, BackupProviderPlugin, BackupSyncPollingInterval, - BackupEnableAttachDetachVolumes + BackupEnableAttachDetachVolumes, + NASBackupParallelExecution }; }