Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
f2a9202
docs: add RFC for incremental NAS backup support (KVM)
jmsperu Apr 27, 2026
1981469
feat(backup): add chain-metadata keys + nas.backup.full.every config
jmsperu Apr 27, 2026
fbb916b
feat(backup): nasbackup.sh full+incremental modes via backup-begin
jmsperu Apr 27, 2026
1f2aebc
feat(backup): orchestrate full vs incremental in NAS provider
jmsperu Apr 27, 2026
43e2f75
feat(backup): on-demand bitmap recreation for incremental NAS backup
jmsperu Apr 27, 2026
39303fb
feat(backup): restore path follows incremental backing-chain
jmsperu Apr 27, 2026
b8d069e
feat(backup): cascade-delete + chain repair for NAS incrementals
jmsperu Apr 27, 2026
49edc7f
test(backup): smoke tests for incremental NAS backup chain
jmsperu Apr 27, 2026
d80ed16
test(backup): mock returns no-backing-chain for rsync-failure test
jmsperu Apr 28, 2026
9764025
docs: move RFC out of repo per reviewer feedback
jmsperu Apr 28, 2026
72f967a
test(backup): mock BackupDetailsDao to fix NPE in NASBackupProviderTest
jmsperu May 5, 2026
7e1691b
feat(backup): anchor incremental chain on VM active_checkpoint_id
jmsperu May 16, 2026
b7b74c4
refactor(backup): mirror snapshot-style delete chain for NAS incremen…
jmsperu May 16, 2026
9f4d61f
test(backup): drop unnecessary host.getId() stub in live-child delete…
jmsperu May 22, 2026
691931d
feat(backup): add nas.backup.incremental.enabled master switch
jmsperu May 22, 2026
0bdcdb1
feat(backup): pre-seed bitmap on stopped-VM backup for next incremental
jmsperu May 22, 2026
5be1910
refactor(backup): per-volume parent paths for incremental NAS backup
jmsperu May 22, 2026
3e2e144
fix(nas-backup): address Copilot review on PR #13074
jmsperu May 26, 2026
4f3375a
fix(nas-backup): don't persist unconfirmed bitmap; gate sanity_checks
jmsperu May 26, 2026
bedcc2a
test(nas-backup): enable incremental in active-checkpoint test
jmsperu May 26, 2026
f2210f4
refactor(nas-backup): address abh1sar review on PR #13074
jmsperu May 27, 2026
3456ba7
backup(nas): address review cleanups — remove dead code + reviewer-me…
jmsperu Jun 10, 2026
ba74778
backup(nas): wrapper uses the passed bitmap name instead of re-parsin…
jmsperu Jun 10, 2026
8adde4d
backup(nas): fix parent-checkpoint recreation for incremental after V…
jmsperu Jun 11, 2026
3ed8a30
backup(nas): regression test — incremental backup survives a VM restart
jmsperu Jun 11, 2026
096bef1
backup(nas): collapse N+1 chain queries when sweeping delete-pending …
jmsperu Jun 13, 2026
73c4206
backup(nas): move backup-mode policy + stdout markers from script to …
jmsperu Jun 13, 2026
4207ea9
test(backup): set CHAIN_POSITION on sweep-up-pending-parent test fixt…
jmsperu Jun 13, 2026
f574628
test(backup): drop unnecessary PARENT_BACKUP_ID stub in sweep-pending…
jmsperu Jun 17, 2026
5f91f86
backup(nas): clear VM active checkpoint on single-volume restore (#13…
jmsperu Jun 22, 2026
8afd170
backup(nas): legacy-full mode — persist nothing when incremental disa…
jmsperu Jun 22, 2026
c331e8f
backup(nas): remove dead rebase op + restore unconditional sanity_che…
jmsperu Jun 22, 2026
901eb40
backup(nas): redefine parent checkpoint with minimal XML, drop persis…
jmsperu Jun 22, 2026
3972b06
backup(nas): self-heal incremental->full in-script, drop EXIT_* signa…
jmsperu Jun 22, 2026
5262f17
backup(nas): fall back to full when the parent bitmap is missing from…
jmsperu Jun 22, 2026
a51f335
backup: account chain-delete resources exactly-once at physical remov…
jmsperu Jun 22, 2026
3f3f242
Merge branch 'main' into feature/nas-backup-incremental
abh1sar Jun 23, 2026
7c2574b
fix(backup): NAS incremental — getPath, all-disk bitmap, Hidden tombs…
jmsperu Jun 28, 2026
cda0417
backup: rename BackupVO.backupType to 'type' so UpdateBuilder tracks …
jmsperu Jun 29, 2026
ae2a6b2
fix(backup): NAS incremental — count parent bitmap per-disk, don't ab…
jmsperu Jul 1, 2026
7263411
Merge branch 'main' into feature/nas-backup-incremental
abh1sar Jul 2, 2026
4552c44
feat(backup): NAS incremental — reclaim parent bitmap + guard non-che…
jmsperu Jul 3, 2026
560c6d3
address review: drop Linstor-incremental branch + PARENT_BITMAP_DELET…
jmsperu Jul 7, 2026
4707ccd
Merge remote-tracking branch 'origin/main' into feature/nas-backup-in…
jmsperu Jul 7, 2026
c010ebb
Merge branch 'main' into feature/nas-backup-incremental
abh1sar Jul 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions core/src/main/java/org/apache/cloudstack/backup/BackupAnswer.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ public class BackupAnswer extends Answer {
private Long virtualSize;
private Map<String, String> volumes;
Boolean needsCleanup;
// Set by the NAS backup provider after a checkpoint/bitmap was created during this backup.
// The provider persists it in backup_details under NASBackupChainKeys.BITMAP_NAME.
private String bitmapCreated;
// Set when an incremental was requested but the agent had to fall back to a full
// (e.g. VM was stopped). Provider should record this backup as type=full.
private Boolean incrementalFallback;
// Set when the agent had to recreate the parent bitmap before this incremental
// (e.g. CloudStack rebuilt the domain XML on the previous VM start, losing bitmaps).
// The first incremental after a recreate is larger than usual; subsequent
// incrementals return to normal size. Informational — recorded in backup_details.
private String bitmapRecreated;

public BackupAnswer(final Command command, final boolean success, final String details) {
super(command, success, details);
Expand Down Expand Up @@ -68,4 +79,28 @@ public Boolean getNeedsCleanup() {
public void setNeedsCleanup(Boolean needsCleanup) {
this.needsCleanup = needsCleanup;
}

public String getBitmapCreated() {
return bitmapCreated;
}

public void setBitmapCreated(String bitmapCreated) {
this.bitmapCreated = bitmapCreated;
}

public Boolean getIncrementalFallback() {
return incrementalFallback != null && incrementalFallback;
}

public void setIncrementalFallback(Boolean incrementalFallback) {
this.incrementalFallback = incrementalFallback;
}

public String getBitmapRecreated() {
return bitmapRecreated;
}

public void setBitmapRecreated(String bitmapRecreated) {
this.bitmapRecreated = bitmapRecreated;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// 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 com.cloud.agent.api.Command;
import com.cloud.agent.api.LogLevel;

/**
* Tells the KVM agent to rebase a NAS backup qcow2 onto a new backing parent. Used by the
* NAS backup provider during chain repair when a middle incremental is being deleted: the
* immediate child must absorb the soon-to-be-deleted parent's blocks and then re-link to
* the grandparent. Both target and new-backing paths are NAS-mount-relative.
*/
public class RebaseBackupCommand extends Command {
Comment thread
abh1sar marked this conversation as resolved.
Outdated
private String targetPath; // mount-relative path of the qcow2 to repoint
private String newBackingPath; // mount-relative path of the new backing parent
private String backupRepoType;
private String backupRepoAddress;
@LogLevel(LogLevel.Log4jLevel.Off)
private String mountOptions;

public RebaseBackupCommand(String targetPath, String newBackingPath,
String backupRepoType, String backupRepoAddress, String mountOptions) {
super();
this.targetPath = targetPath;
this.newBackingPath = newBackingPath;
this.backupRepoType = backupRepoType;
this.backupRepoAddress = backupRepoAddress;
this.mountOptions = mountOptions;
}

public String getTargetPath() {
return targetPath;
}

public String getNewBackingPath() {
return newBackingPath;
}

public String getBackupRepoType() {
return backupRepoType;
}

public String getBackupRepoAddress() {
return backupRepoAddress;
}

public String getMountOptions() {
return mountOptions == null ? "" : mountOptions;
}

@Override
public boolean executeInSequence() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ public class TakeBackupCommand extends Command {
@LogLevel(LogLevel.Log4jLevel.Off)
private String mountOptions;

// Incremental backup fields (NAS provider; null/empty for legacy full-only callers).
private String mode; // "full" or "incremental"; null => legacy behaviour (script default)
private String bitmapNew; // Checkpoint/bitmap name to create with this backup (timestamp-based)
private String bitmapParent; // Incremental: parent bitmap to read changes since
private String parentPath; // Incremental: parent backup file path on the mounted NAS (for qemu-img rebase)

public TakeBackupCommand(String vmName, String backupPath) {
super();
this.vmName = vmName;
Expand Down Expand Up @@ -106,6 +112,38 @@ public void setQuiesce(Boolean quiesce) {
this.quiesce = quiesce;
}

public String getMode() {
return mode;
}

public void setMode(String mode) {
this.mode = mode;
}

public String getBitmapNew() {
return bitmapNew;
}

public void setBitmapNew(String bitmapNew) {
this.bitmapNew = bitmapNew;
}

public String getBitmapParent() {
return bitmapParent;
}

public void setBitmapParent(String bitmapParent) {
this.bitmapParent = bitmapParent;
}

public String getParentPath() {
return parentPath;
}

public void setParentPath(String parentPath) {
this.parentPath = parentPath;
}

@Override
public boolean executeInSequence() {
return true;
Expand Down
Loading
Loading