mirror of https://github.com/apache/cloudstack.git
Basic working version-1
This commit is contained in:
parent
81c3b5ba0b
commit
f396c5cc74
|
|
@ -0,0 +1,87 @@
|
|||
//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.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.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.command.admin.AdminCmd;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.ImageTransferResponse;
|
||||
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||
import org.apache.cloudstack.backup.IncrementalBackupService;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
@APICommand(name = "createImageTransfer",
|
||||
description = "Create image transfer for a disk in backup",
|
||||
responseObject = ImageTransferResponse.class,
|
||||
since = "4.22.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class CreateImageTransferCmd extends BaseCmd implements AdminCmd {
|
||||
|
||||
@Inject
|
||||
private IncrementalBackupService incrementalBackupService;
|
||||
|
||||
@Parameter(name = ApiConstants.BACKUP_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = BackupResponse.class,
|
||||
required = true,
|
||||
description = "ID of the backup")
|
||||
private Long backupId;
|
||||
|
||||
@Parameter(name = ApiConstants.VOLUME_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = VolumeResponse.class,
|
||||
required = true,
|
||||
description = "ID of the disk/volume")
|
||||
private Long volumeId;
|
||||
|
||||
@Parameter(name = ApiConstants.DIRECTION,
|
||||
type = CommandType.STRING,
|
||||
required = true,
|
||||
description = "Direction of the transfer: upload, download")
|
||||
private String direction;
|
||||
|
||||
public Long getBackupId() {
|
||||
return backupId;
|
||||
}
|
||||
|
||||
public Long getVolumeId() {
|
||||
return volumeId;
|
||||
}
|
||||
|
||||
public String getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
ImageTransferResponse response = incrementalBackupService.createImageTransfer(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
//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.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.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.command.admin.AdminCmd;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.backup.IncrementalBackupService;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
@APICommand(name = "deleteVmCheckpoint",
|
||||
description = "Delete a VM checkpoint",
|
||||
responseObject = SuccessResponse.class,
|
||||
since = "4.22.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class DeleteVmCheckpointCmd extends BaseCmd implements AdminCmd {
|
||||
|
||||
@Inject
|
||||
private IncrementalBackupService incrementalBackupService;
|
||||
|
||||
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = UserVmResponse.class,
|
||||
required = true,
|
||||
description = "ID of the VM")
|
||||
private Long vmId;
|
||||
|
||||
@Parameter(name = "checkpointid",
|
||||
type = CommandType.STRING,
|
||||
required = true,
|
||||
description = "Checkpoint ID")
|
||||
private String checkpointId;
|
||||
|
||||
public Long getVmId() {
|
||||
return vmId;
|
||||
}
|
||||
|
||||
public String getCheckpointId() {
|
||||
return checkpointId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
boolean result = incrementalBackupService.deleteVmCheckpoint(this);
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
response.setSuccess(result);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
//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.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.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.command.admin.AdminCmd;
|
||||
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.IncrementalBackupService;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
@APICommand(name = "finalizeBackup",
|
||||
description = "Finalize a VM backup session",
|
||||
responseObject = SuccessResponse.class,
|
||||
since = "4.22.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class FinalizeBackupCmd extends BaseCmd implements AdminCmd {
|
||||
|
||||
@Inject
|
||||
private IncrementalBackupService incrementalBackupService;
|
||||
|
||||
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = UserVmResponse.class,
|
||||
required = true,
|
||||
description = "ID of the VM")
|
||||
private Long vmId;
|
||||
|
||||
@Parameter(name = ApiConstants.ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = BackupResponse.class,
|
||||
required = true,
|
||||
description = "ID of the backup")
|
||||
private Long backupId;
|
||||
|
||||
public Long getVmId() {
|
||||
return vmId;
|
||||
}
|
||||
|
||||
public Long getBackupId() {
|
||||
return backupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
boolean result = incrementalBackupService.finalizeBackup(this);
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
response.setSuccess(result);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
//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.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.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.command.admin.AdminCmd;
|
||||
import org.apache.cloudstack.api.response.ImageTransferResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.backup.IncrementalBackupService;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
@APICommand(name = "finalizeImageTransfer",
|
||||
description = "Finalize an image transfer",
|
||||
responseObject = SuccessResponse.class,
|
||||
since = "4.22.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class FinalizeImageTransferCmd extends BaseCmd implements AdminCmd {
|
||||
|
||||
@Inject
|
||||
private IncrementalBackupService incrementalBackupService;
|
||||
|
||||
@Parameter(name = ApiConstants.ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = ImageTransferResponse.class,
|
||||
required = true,
|
||||
description = "ID of the image transfer")
|
||||
private Long imageTransferId;
|
||||
|
||||
public Long getImageTransferId() {
|
||||
return imageTransferId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
boolean result = incrementalBackupService.finalizeImageTransfer(this);
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
response.setSuccess(result);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
//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.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.BaseListCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.command.admin.AdminCmd;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.ImageTransferResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.backup.IncrementalBackupService;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
@APICommand(name = "listImageTransfers",
|
||||
description = "List image transfers for a backup",
|
||||
responseObject = ImageTransferResponse.class,
|
||||
since = "4.22.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class ListImageTransfersCmd extends BaseListCmd implements AdminCmd {
|
||||
|
||||
@Inject
|
||||
private IncrementalBackupService incrementalBackupService;
|
||||
|
||||
@Parameter(name = ApiConstants.ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = ImageTransferResponse.class,
|
||||
description = "ID of the Image Transfer")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.BACKUP_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = BackupResponse.class,
|
||||
description = "ID of the backup")
|
||||
private Long backupId;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Long getBackupId() {
|
||||
return backupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
List<ImageTransferResponse> responses = incrementalBackupService.listImageTransfers(this);
|
||||
ListResponse<ImageTransferResponse> response = new ListResponse<>();
|
||||
response.setResponses(responses);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
//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.BaseListCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.command.admin.AdminCmd;
|
||||
import org.apache.cloudstack.api.response.CheckpointResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.backup.IncrementalBackupService;
|
||||
|
||||
@APICommand(name = "listVmCheckpoints",
|
||||
description = "List checkpoints for a VM",
|
||||
responseObject = CheckpointResponse.class,
|
||||
since = "4.22.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class ListVmCheckpointsCmd extends BaseListCmd implements AdminCmd {
|
||||
|
||||
@Inject
|
||||
private IncrementalBackupService incrementalBackupService;
|
||||
|
||||
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = UserVmResponse.class,
|
||||
required = true,
|
||||
description = "ID of the VM")
|
||||
private Long vmId;
|
||||
|
||||
public Long getVmId() {
|
||||
return vmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
List<CheckpointResponse> responses = incrementalBackupService.listVmCheckpoints(this);
|
||||
ListResponse<CheckpointResponse> response = new ListResponse<>();
|
||||
response.setResponses(responses);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
//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.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.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.command.admin.AdminCmd;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.backup.IncrementalBackupService;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
|
||||
@APICommand(name = "startBackup",
|
||||
description = "Start a VM backup session (oVirt-style incremental backup)",
|
||||
responseObject = BackupResponse.class,
|
||||
since = "4.22.0",
|
||||
authorized = {RoleType.Admin})
|
||||
public class StartBackupCmd extends BaseCmd implements AdminCmd {
|
||||
|
||||
@Inject
|
||||
private IncrementalBackupService incrementalBackupService;
|
||||
|
||||
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = UserVmResponse.class,
|
||||
required = true,
|
||||
description = "ID of the VM")
|
||||
private Long vmId;
|
||||
|
||||
public Long getVmId() {
|
||||
return vmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
BackupResponse response = incrementalBackupService.startBackup(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -127,6 +127,18 @@ public class BackupResponse extends BaseResponse {
|
|||
@Param(description = "Indicates whether the VM from which the backup was taken is expunged or not", since = "4.22.0")
|
||||
private Boolean isVmExpunged;
|
||||
|
||||
@SerializedName("from_checkpoint_id")
|
||||
@Param(description = "Previous active checkpoint id for incremental backups", since = "4.22.0")
|
||||
private String fromCheckpointId;
|
||||
|
||||
@SerializedName("to_checkpoint_id")
|
||||
@Param(description = "Next checkpoint id for incremental backups", since = "4.22.0")
|
||||
private String toCheckpointId;
|
||||
|
||||
@SerializedName(ApiConstants.HOST_ID)
|
||||
@Param(description = "Host ID where the backup is running", since = "4.22.0")
|
||||
private String hostId;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
|
@ -314,4 +326,28 @@ public class BackupResponse extends BaseResponse {
|
|||
public void setVmExpunged(Boolean isVmExpunged) {
|
||||
this.isVmExpunged = isVmExpunged;
|
||||
}
|
||||
|
||||
public void setFromCheckpointId(String fromCheckpointId) {
|
||||
this.fromCheckpointId = fromCheckpointId;
|
||||
}
|
||||
|
||||
public String getFromCheckpointId() {
|
||||
return this.fromCheckpointId;
|
||||
}
|
||||
|
||||
public void setToCheckpointId(String toCheckpointId) {
|
||||
this.toCheckpointId = toCheckpointId;
|
||||
}
|
||||
|
||||
public String getToCheckpointId() {
|
||||
return this.toCheckpointId;
|
||||
}
|
||||
|
||||
public void setHostId(String hostId) {
|
||||
this.hostId = hostId;
|
||||
}
|
||||
|
||||
public String getHostId() {
|
||||
return this.hostId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
//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.api.response;
|
||||
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
|
||||
import com.cloud.serializer.Param;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class CheckpointResponse extends BaseResponse {
|
||||
|
||||
@SerializedName("checkpointid")
|
||||
@Param(description = "the checkpoint ID")
|
||||
private String checkpointId;
|
||||
|
||||
@SerializedName("createtime")
|
||||
@Param(description = "the checkpoint creation time")
|
||||
private Long createTime;
|
||||
|
||||
@SerializedName("isactive")
|
||||
@Param(description = "whether this is the active checkpoint")
|
||||
private Boolean isActive;
|
||||
|
||||
public void setCheckpointId(String checkpointId) {
|
||||
this.checkpointId = checkpointId;
|
||||
}
|
||||
|
||||
public void setCreateTime(Long createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public void setIsActive(Boolean isActive) {
|
||||
this.isActive = isActive;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
//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.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.ImageTransfer;
|
||||
|
||||
import com.cloud.serializer.Param;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
@EntityReference(value = ImageTransfer.class)
|
||||
public class ImageTransferResponse extends BaseResponse {
|
||||
|
||||
@SerializedName(ApiConstants.ID)
|
||||
@Param(description = "the ID of the image transfer")
|
||||
private String id;
|
||||
|
||||
@SerializedName("backupid")
|
||||
@Param(description = "the backup ID")
|
||||
private String backupId;
|
||||
|
||||
@SerializedName("vmid")
|
||||
@Param(description = "the VM ID")
|
||||
private String vmId;
|
||||
|
||||
@SerializedName(ApiConstants.VOLUME_ID)
|
||||
@Param(description = "the disk/volume ID")
|
||||
private String diskId;
|
||||
|
||||
@SerializedName("devicename")
|
||||
@Param(description = "the device name (vda, vdb, etc)")
|
||||
private String deviceName;
|
||||
|
||||
@SerializedName("transferurl")
|
||||
@Param(description = "the transfer URL")
|
||||
private String transferUrl;
|
||||
|
||||
@SerializedName("phase")
|
||||
@Param(description = "the transfer phase")
|
||||
private String phase;
|
||||
|
||||
@SerializedName("direction")
|
||||
@Param(description = "the image transfer direction: upload / download")
|
||||
private String direction;
|
||||
|
||||
@SerializedName(ApiConstants.CREATED)
|
||||
@Param(description = "the date created")
|
||||
private Date created;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setBackupId(String backupId) {
|
||||
this.backupId = backupId;
|
||||
}
|
||||
|
||||
public void setVmId(String vmId) {
|
||||
this.vmId = vmId;
|
||||
}
|
||||
|
||||
public void setDiskId(String diskId) {
|
||||
this.diskId = diskId;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
|
||||
public void setTransferUrl(String transferUrl) {
|
||||
this.transferUrl = transferUrl;
|
||||
}
|
||||
|
||||
public void setPhase(String phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
public void setDirection(String direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,16 @@ import com.cloud.storage.Volume;
|
|||
|
||||
public interface Backup extends ControlledEntity, InternalIdentity, Identity {
|
||||
|
||||
String getFromCheckpointId();
|
||||
|
||||
String getToCheckpointId();
|
||||
|
||||
Long getCheckpointCreateTime();
|
||||
|
||||
Long getHostId();
|
||||
|
||||
Integer getNbdPort();
|
||||
|
||||
enum Status {
|
||||
Allocated, Queued, BackingUp, BackedUp, Error, Failed, Restoring, Removed, Expunged
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.backup;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
public interface ImageTransfer extends ControlledEntity, InternalIdentity {
|
||||
public enum Direction {
|
||||
upload, download
|
||||
}
|
||||
|
||||
public enum Phase {
|
||||
initializing, transferring, finished, failed
|
||||
}
|
||||
|
||||
String getUuid();
|
||||
|
||||
long getBackupId();
|
||||
|
||||
long getVmId();
|
||||
|
||||
long getDiskId();
|
||||
|
||||
String getDeviceName();
|
||||
|
||||
long getHostId();
|
||||
|
||||
int getNbdPort();
|
||||
|
||||
String getTransferUrl();
|
||||
|
||||
Phase getPhase();
|
||||
|
||||
Direction getDirection();
|
||||
|
||||
String getSignedTicketId();
|
||||
}
|
||||
|
|
@ -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
|
||||
//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.CreateImageTransferCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.DeleteVmCheckpointCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.FinalizeBackupCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.FinalizeImageTransferCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.ListImageTransfersCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.ListVmCheckpointsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.StartBackupCmd;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.CheckpointResponse;
|
||||
import org.apache.cloudstack.api.response.ImageTransferResponse;
|
||||
|
||||
import com.cloud.utils.component.PluggableService;
|
||||
|
||||
/**
|
||||
* Service for managing oVirt-style incremental backups using libvirt checkpoints
|
||||
*/
|
||||
public interface IncrementalBackupService extends PluggableService {
|
||||
|
||||
/**
|
||||
* Start a backup session for a VM
|
||||
* Creates a new checkpoint and starts NBD server for pull-mode backup
|
||||
*/
|
||||
BackupResponse startBackup(StartBackupCmd cmd);
|
||||
|
||||
/**
|
||||
* Finalize a backup session
|
||||
* Stops NBD server, updates checkpoint tracking, deletes old checkpoints
|
||||
*/
|
||||
boolean finalizeBackup(FinalizeBackupCmd cmd);
|
||||
|
||||
/**
|
||||
* Create an image transfer object for a disk
|
||||
* Registers NBD endpoint with ImageIO (stubbed for POC)
|
||||
*/
|
||||
ImageTransferResponse createImageTransfer(CreateImageTransferCmd cmd);
|
||||
|
||||
/**
|
||||
* Finalize an image transfer
|
||||
* Marks transfer as complete (NBD is closed globally in finalize backup)
|
||||
*/
|
||||
boolean finalizeImageTransfer(FinalizeImageTransferCmd cmd);
|
||||
|
||||
/**
|
||||
* List image transfers for a backup
|
||||
*/
|
||||
List<ImageTransferResponse> listImageTransfers(ListImageTransfersCmd cmd);
|
||||
|
||||
/**
|
||||
* List checkpoints for a VM
|
||||
*/
|
||||
List<CheckpointResponse> listVmCheckpoints(ListVmCheckpointsCmd cmd);
|
||||
|
||||
/**
|
||||
* Delete a VM checkpoint (no-op for normal flow, kept for API parity)
|
||||
*/
|
||||
boolean deleteVmCheckpoint(DeleteVmCheckpointCmd cmd);
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
//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 com.cloud.agent.api.Answer;
|
||||
|
||||
public class CreateImageTransferAnswer extends Answer {
|
||||
private String imageTransferId;
|
||||
private String transferUrl;
|
||||
private String phase;
|
||||
|
||||
public CreateImageTransferAnswer() {
|
||||
}
|
||||
|
||||
public CreateImageTransferAnswer(CreateImageTransferCommand cmd, boolean success, String details) {
|
||||
super(cmd, success, details);
|
||||
}
|
||||
|
||||
public CreateImageTransferAnswer(CreateImageTransferCommand cmd, boolean success, String details,
|
||||
String imageTransferId, String transferUrl, String phase) {
|
||||
super(cmd, success, details);
|
||||
this.imageTransferId = imageTransferId;
|
||||
this.transferUrl = transferUrl;
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
public String getImageTransferId() {
|
||||
return imageTransferId;
|
||||
}
|
||||
|
||||
public void setImageTransferId(String imageTransferId) {
|
||||
this.imageTransferId = imageTransferId;
|
||||
}
|
||||
|
||||
public String getTransferUrl() {
|
||||
return transferUrl;
|
||||
}
|
||||
|
||||
public void setTransferUrl(String transferUrl) {
|
||||
this.transferUrl = transferUrl;
|
||||
}
|
||||
|
||||
public String getPhase() {
|
||||
return phase;
|
||||
}
|
||||
|
||||
public void setPhase(String phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
//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 com.cloud.agent.api.Command;
|
||||
|
||||
public class CreateImageTransferCommand extends Command {
|
||||
private Long vmId;
|
||||
private Long backupId;
|
||||
private Long diskId;
|
||||
private String deviceName;
|
||||
private int nbdPort;
|
||||
|
||||
public CreateImageTransferCommand() {
|
||||
}
|
||||
|
||||
public CreateImageTransferCommand(Long vmId, Long backupId, Long diskId, String deviceName, int nbdPort) {
|
||||
this.vmId = vmId;
|
||||
this.backupId = backupId;
|
||||
this.diskId = diskId;
|
||||
this.deviceName = deviceName;
|
||||
this.nbdPort = nbdPort;
|
||||
}
|
||||
|
||||
public Long getVmId() {
|
||||
return vmId;
|
||||
}
|
||||
|
||||
public Long getBackupId() {
|
||||
return backupId;
|
||||
}
|
||||
|
||||
public Long getDiskId() {
|
||||
return diskId;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public int getNbdPort() {
|
||||
return nbdPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
//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.Map;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
|
||||
public class StartBackupAnswer extends Answer {
|
||||
private Long checkpointCreateTime;
|
||||
private Map<Long, String> deviceMappings; // volumeId -> device name (vda, vdb, etc.)
|
||||
|
||||
public StartBackupAnswer() {
|
||||
}
|
||||
|
||||
public StartBackupAnswer(StartBackupCommand cmd, boolean success, String details) {
|
||||
super(cmd, success, details);
|
||||
}
|
||||
|
||||
public StartBackupAnswer(StartBackupCommand cmd, boolean success, String details,
|
||||
Long checkpointCreateTime, Map<Long, String> deviceMappings) {
|
||||
super(cmd, success, details);
|
||||
this.checkpointCreateTime = checkpointCreateTime;
|
||||
this.deviceMappings = deviceMappings;
|
||||
}
|
||||
|
||||
public Long getCheckpointCreateTime() {
|
||||
return checkpointCreateTime;
|
||||
}
|
||||
|
||||
public void setCheckpointCreateTime(Long checkpointCreateTime) {
|
||||
this.checkpointCreateTime = checkpointCreateTime;
|
||||
}
|
||||
|
||||
public Map<Long, String> getDeviceMappings() {
|
||||
return deviceMappings;
|
||||
}
|
||||
|
||||
public void setDeviceMappings(Map<Long, String> deviceMappings) {
|
||||
this.deviceMappings = deviceMappings;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
//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.Map;
|
||||
|
||||
import com.cloud.agent.api.Command;
|
||||
|
||||
public class StartBackupCommand extends Command {
|
||||
private String vmName;
|
||||
private Long vmId;
|
||||
private String toCheckpointId;
|
||||
private String fromCheckpointId;
|
||||
private int nbdPort;
|
||||
private Map<Long, String> diskVolumePaths; // volumeId -> path mapping
|
||||
|
||||
public StartBackupCommand() {
|
||||
}
|
||||
|
||||
public StartBackupCommand(String vmName, Long vmId, String toCheckpointId, String fromCheckpointId,
|
||||
int nbdPort, Map<Long, String> diskVolumePaths) {
|
||||
this.vmName = vmName;
|
||||
this.vmId = vmId;
|
||||
this.toCheckpointId = toCheckpointId;
|
||||
this.fromCheckpointId = fromCheckpointId;
|
||||
this.nbdPort = nbdPort;
|
||||
this.diskVolumePaths = diskVolumePaths;
|
||||
}
|
||||
|
||||
public String getVmName() {
|
||||
return vmName;
|
||||
}
|
||||
|
||||
public Long getVmId() {
|
||||
return vmId;
|
||||
}
|
||||
|
||||
public String getToCheckpointId() {
|
||||
return toCheckpointId;
|
||||
}
|
||||
|
||||
public String getFromCheckpointId() {
|
||||
return fromCheckpointId;
|
||||
}
|
||||
|
||||
public int getNbdPort() {
|
||||
return nbdPort;
|
||||
}
|
||||
|
||||
public Map<Long, String> getDiskVolumePaths() {
|
||||
return diskVolumePaths;
|
||||
}
|
||||
|
||||
public boolean isIncremental() {
|
||||
return fromCheckpointId != null && !fromCheckpointId.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//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 com.cloud.agent.api.Answer;
|
||||
|
||||
public class StopBackupAnswer extends Answer {
|
||||
|
||||
public StopBackupAnswer() {
|
||||
}
|
||||
|
||||
public StopBackupAnswer(StopBackupCommand cmd, boolean success, String details) {
|
||||
super(cmd, success, details);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
//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 com.cloud.agent.api.Command;
|
||||
|
||||
public class StopBackupCommand extends Command {
|
||||
private String vmName;
|
||||
private Long vmId;
|
||||
private Long backupId;
|
||||
|
||||
public StopBackupCommand() {
|
||||
}
|
||||
|
||||
public StopBackupCommand(String vmName, Long vmId, Long backupId) {
|
||||
this.vmName = vmName;
|
||||
this.vmId = vmId;
|
||||
this.backupId = backupId;
|
||||
}
|
||||
|
||||
public String getVmName() {
|
||||
return vmName;
|
||||
}
|
||||
|
||||
public Long getVmId() {
|
||||
return vmId;
|
||||
}
|
||||
|
||||
public Long getBackupId() {
|
||||
return backupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -202,6 +202,12 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject<State, Vi
|
|||
@Column(name = "backup_volumes", length = 65535)
|
||||
protected String backupVolumes;
|
||||
|
||||
@Column(name = "active_checkpoint_id")
|
||||
protected String activeCheckpointId;
|
||||
|
||||
@Column(name = "active_checkpoint_create_time")
|
||||
protected Long activeCheckpointCreateTime;
|
||||
|
||||
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;
|
||||
|
|
@ -628,4 +634,20 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject<State, Vi
|
|||
public void setBackupVolumes(String backupVolumes) {
|
||||
this.backupVolumes = backupVolumes;
|
||||
}
|
||||
|
||||
public String getActiveCheckpointId() {
|
||||
return activeCheckpointId;
|
||||
}
|
||||
|
||||
public void setActiveCheckpointId(String activeCheckpointId) {
|
||||
this.activeCheckpointId = activeCheckpointId;
|
||||
}
|
||||
|
||||
public Long getActiveCheckpointCreateTime() {
|
||||
return activeCheckpointCreateTime;
|
||||
}
|
||||
|
||||
public void setActiveCheckpointCreateTime(Long activeCheckpointCreateTime) {
|
||||
this.activeCheckpointCreateTime = activeCheckpointCreateTime;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,21 @@ public class BackupVO implements Backup {
|
|||
@Column(name = "backup_schedule_id")
|
||||
private Long backupScheduleId;
|
||||
|
||||
@Column(name = "from_checkpoint_id")
|
||||
private String fromCheckpointId;
|
||||
|
||||
@Column(name = "to_checkpoint_id")
|
||||
private String toCheckpointId;
|
||||
|
||||
@Column(name = "checkpoint_create_time")
|
||||
private Long checkpointCreateTime;
|
||||
|
||||
@Column(name = "host_id")
|
||||
private Long hostId;
|
||||
|
||||
@Column(name = "nbd_port")
|
||||
private Integer nbdPort;
|
||||
|
||||
@Transient
|
||||
Map<String, String> details;
|
||||
|
||||
|
|
@ -288,4 +303,49 @@ public class BackupVO implements Backup {
|
|||
public void setBackupScheduleId(Long backupScheduleId) {
|
||||
this.backupScheduleId = backupScheduleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFromCheckpointId() {
|
||||
return fromCheckpointId;
|
||||
}
|
||||
|
||||
public void setFromCheckpointId(String fromCheckpointId) {
|
||||
this.fromCheckpointId = fromCheckpointId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToCheckpointId() {
|
||||
return toCheckpointId;
|
||||
}
|
||||
|
||||
public void setToCheckpointId(String toCheckpointId) {
|
||||
this.toCheckpointId = toCheckpointId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getCheckpointCreateTime() {
|
||||
return checkpointCreateTime;
|
||||
}
|
||||
|
||||
public void setCheckpointCreateTime(Long checkpointCreateTime) {
|
||||
this.checkpointCreateTime = checkpointCreateTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getHostId() {
|
||||
return hostId;
|
||||
}
|
||||
|
||||
public void setHostId(Long hostId) {
|
||||
this.hostId = hostId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getNbdPort() {
|
||||
return nbdPort;
|
||||
}
|
||||
|
||||
public void setNbdPort(Integer nbdPort) {
|
||||
this.nbdPort = nbdPort;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,243 @@
|
|||
//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 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;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
@Entity
|
||||
@Table(name = "image_transfer")
|
||||
public class ImageTransferVO implements ImageTransfer {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private long id;
|
||||
|
||||
@Column(name = "uuid")
|
||||
private String uuid;
|
||||
|
||||
@Column(name = "backup_id")
|
||||
private long backupId;
|
||||
|
||||
@Column(name = "vm_id")
|
||||
private long vmId;
|
||||
|
||||
@Column(name = "disk_id")
|
||||
private long diskId;
|
||||
|
||||
@Column(name = "device_name")
|
||||
private String deviceName;
|
||||
|
||||
@Column(name = "host_id")
|
||||
private long hostId;
|
||||
|
||||
@Column(name = "nbd_port")
|
||||
private int nbdPort;
|
||||
|
||||
@Column(name = "transfer_url")
|
||||
private String transferUrl;
|
||||
|
||||
@Enumerated(value = EnumType.STRING)
|
||||
@Column(name = "phase")
|
||||
private Phase phase;
|
||||
|
||||
@Enumerated(value = EnumType.STRING)
|
||||
@Column(name = "direction")
|
||||
private Direction direction;
|
||||
|
||||
@Column(name = "signed_ticket_id")
|
||||
private String signedTicketId;
|
||||
|
||||
@Column(name = "account_id")
|
||||
Long accountId;
|
||||
|
||||
@Column(name = "domain_id")
|
||||
Long domainId;
|
||||
|
||||
@Column(name = "created")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date created;
|
||||
|
||||
@Column(name = "updated")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date updated;
|
||||
|
||||
@Column(name = "removed")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date removed;
|
||||
|
||||
public ImageTransferVO() {
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public ImageTransferVO(long backupId, long vmId, long diskId, String deviceName, long hostId, int nbdPort, Phase phase, Direction direction, Long accountId, Long domainId) {
|
||||
this();
|
||||
this.backupId = backupId;
|
||||
this.vmId = vmId;
|
||||
this.diskId = diskId;
|
||||
this.deviceName = deviceName;
|
||||
this.hostId = hostId;
|
||||
this.nbdPort = nbdPort;
|
||||
this.phase = phase;
|
||||
this.direction = direction;
|
||||
this.accountId = accountId;
|
||||
this.domainId = domainId;
|
||||
this.created = new Date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBackupId() {
|
||||
return backupId;
|
||||
}
|
||||
|
||||
public void setBackupId(long backupId) {
|
||||
this.backupId = backupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVmId() {
|
||||
return vmId;
|
||||
}
|
||||
|
||||
public void setVmId(long vmId) {
|
||||
this.vmId = vmId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDiskId() {
|
||||
return diskId;
|
||||
}
|
||||
|
||||
public void setDiskId(long diskId) {
|
||||
this.diskId = diskId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getHostId() {
|
||||
return hostId;
|
||||
}
|
||||
|
||||
public void setHostId(long hostId) {
|
||||
this.hostId = hostId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNbdPort() {
|
||||
return nbdPort;
|
||||
}
|
||||
|
||||
public void setNbdPort(int nbdPort) {
|
||||
this.nbdPort = nbdPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTransferUrl() {
|
||||
return transferUrl;
|
||||
}
|
||||
|
||||
public void setTransferUrl(String transferUrl) {
|
||||
this.transferUrl = transferUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Phase getPhase() {
|
||||
return phase;
|
||||
}
|
||||
|
||||
public void setPhase(Phase phase) {
|
||||
this.phase = phase;
|
||||
this.updated = new Date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setDirection(Direction direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSignedTicketId() {
|
||||
return signedTicketId;
|
||||
}
|
||||
|
||||
public void setSignedTicketId(String signedTicketId) {
|
||||
this.signedTicketId = signedTicketId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getEntityType() {
|
||||
return ImageTransfer.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public Date getUpdated() {
|
||||
return updated;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//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.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.backup.ImageTransferVO;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface ImageTransferDao extends GenericDao<ImageTransferVO, Long> {
|
||||
List<ImageTransferVO> listByBackupId(Long backupId);
|
||||
List<ImageTransferVO> listByVmId(Long vmId);
|
||||
ImageTransferVO findByUuid(String uuid);
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
//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.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.apache.cloudstack.backup.ImageTransferVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
@Component
|
||||
public class ImageTransferDaoImpl extends GenericDaoBase<ImageTransferVO, Long> implements ImageTransferDao {
|
||||
|
||||
private SearchBuilder<ImageTransferVO> backupIdSearch;
|
||||
private SearchBuilder<ImageTransferVO> vmIdSearch;
|
||||
private SearchBuilder<ImageTransferVO> uuidSearch;
|
||||
|
||||
public ImageTransferDaoImpl() {
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
protected void init() {
|
||||
backupIdSearch = createSearchBuilder();
|
||||
backupIdSearch.and("backupId", backupIdSearch.entity().getBackupId(), SearchCriteria.Op.EQ);
|
||||
backupIdSearch.done();
|
||||
|
||||
vmIdSearch = createSearchBuilder();
|
||||
vmIdSearch.and("vmId", vmIdSearch.entity().getVmId(), SearchCriteria.Op.EQ);
|
||||
vmIdSearch.done();
|
||||
|
||||
uuidSearch = createSearchBuilder();
|
||||
uuidSearch.and("uuid", uuidSearch.entity().getUuid(), SearchCriteria.Op.EQ);
|
||||
uuidSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImageTransferVO> listByBackupId(Long backupId) {
|
||||
SearchCriteria<ImageTransferVO> sc = backupIdSearch.create();
|
||||
sc.setParameters("backupId", backupId);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImageTransferVO> listByVmId(Long vmId) {
|
||||
SearchCriteria<ImageTransferVO> sc = vmIdSearch.create();
|
||||
sc.setParameters("vmId", vmId);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageTransferVO findByUuid(String uuid) {
|
||||
SearchCriteria<ImageTransferVO> sc = uuidSearch.create();
|
||||
sc.setParameters("uuid", uuid);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
}
|
||||
|
|
@ -273,6 +273,7 @@
|
|||
<bean id="backupDaoImpl" class="org.apache.cloudstack.backup.dao.BackupDaoImpl" />
|
||||
<bean id="backupDetailsDaoImpl" class="org.apache.cloudstack.backup.dao.BackupDetailsDaoImpl" />
|
||||
<bean id="backupRepositoryDaoImpl" class="org.apache.cloudstack.backup.dao.BackupRepositoryDaoImpl" />
|
||||
<bean id="imageTransferDaoImpl" class="org.apache.cloudstack.backup.dao.ImageTransferDaoImpl" />
|
||||
<bean id="directDownloadCertificateDaoImpl" class="org.apache.cloudstack.direct.download.DirectDownloadCertificateDaoImpl" />
|
||||
<bean id="directDownloadCertificateHostMapDaoImpl" class="org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMapDaoImpl" />
|
||||
<bean id="routerHealthCheckResultsDaoImpl" class="com.cloud.network.dao.RouterHealthCheckResultDaoImpl" />
|
||||
|
|
|
|||
|
|
@ -117,3 +117,43 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','conserve_mode', 'tin
|
|||
|
||||
--- Disable/enable NICs
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nics','enabled', 'TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''Indicates whether the NIC is enabled or not'' ');
|
||||
|
||||
-- Add checkpoint tracking fields to backups table for incremental backup support
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'from_checkpoint_id', 'VARCHAR(255) DEFAULT NULL COMMENT "Previous active checkpoint id for incremental backups"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'to_checkpoint_id', 'VARCHAR(255) DEFAULT NULL COMMENT "New checkpoint id created for this backup session"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'checkpoint_create_time', 'BIGINT DEFAULT NULL COMMENT "Checkpoint creation timestamp from libvirt"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'host_id', 'BIGINT UNSIGNED DEFAULT NULL COMMENT "Host where backup is running"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'nbd_port', 'INT DEFAULT NULL COMMENT "NBD server port for backup"');
|
||||
|
||||
-- Add checkpoint tracking fields to vm_instance table for domain recreation
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_instance', 'active_checkpoint_id', 'VARCHAR(255) DEFAULT NULL COMMENT "Active checkpoint id tracked for incremental backups"');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_instance', 'active_checkpoint_create_time', 'BIGINT DEFAULT NULL COMMENT "Active checkpoint creation time"');
|
||||
|
||||
-- Create image_transfer table for per-disk image transfers
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`image_transfer`(
|
||||
`id` bigint unsigned NOT NULL auto_increment COMMENT 'id',
|
||||
`uuid` varchar(40) NOT NULL COMMENT 'uuid',
|
||||
`account_id` bigint unsigned NOT NULL COMMENT 'Account ID',
|
||||
`domain_id` bigint unsigned NOT NULL COMMENT 'Domain ID',
|
||||
`backup_id` bigint unsigned NOT NULL COMMENT 'Backup ID',
|
||||
`vm_id` bigint unsigned NOT NULL COMMENT 'VM ID',
|
||||
`disk_id` bigint unsigned NOT NULL COMMENT 'Disk/Volume ID',
|
||||
`device_name` varchar(10) NOT NULL COMMENT 'Device name (vda, vdb, etc)',
|
||||
`host_id` bigint unsigned NOT NULL COMMENT 'Host ID',
|
||||
`nbd_port` int NOT NULL COMMENT 'NBD port',
|
||||
`transfer_url` varchar(255) COMMENT 'ImageIO transfer URL',
|
||||
`phase` varchar(20) NOT NULL COMMENT 'Transfer phase: initializing, transferring, finished, failed',
|
||||
`direction` varchar(20) NOT NULL COMMENT 'Direction: upload, download',
|
||||
`signed_ticket_id` varchar(255) COMMENT 'Signed ticket ID from ImageIO',
|
||||
`created` datetime NOT NULL COMMENT 'date created',
|
||||
`updated` datetime COMMENT 'date updated if not null',
|
||||
`removed` datetime COMMENT 'date removed if not null',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uuid` (`uuid`),
|
||||
CONSTRAINT `fk_image_transfer__backup_id` FOREIGN KEY (`backup_id`) REFERENCES `backups`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_image_transfer__vm_id` FOREIGN KEY (`vm_id`) REFERENCES `vm_instance`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_image_transfer__disk_id` FOREIGN KEY (`disk_id`) REFERENCES `volumes`(`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_image_transfer__host_id` FOREIGN KEY (`host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE,
|
||||
INDEX `i_image_transfer__backup_id`(`backup_id`),
|
||||
INDEX `i_image_transfer__vm_id`(`vm_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
//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.hypervisor.kvm.resource.wrapper;
|
||||
|
||||
import org.apache.cloudstack.backup.CreateImageTransferAnswer;
|
||||
import org.apache.cloudstack.backup.CreateImageTransferCommand;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
|
||||
@ResourceWrapper(handles = CreateImageTransferCommand.class)
|
||||
public class LibvirtCreateImageTransferCommandWrapper extends CommandWrapper<CreateImageTransferCommand, Answer, LibvirtComputingResource> {
|
||||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
public Answer execute(CreateImageTransferCommand cmd, LibvirtComputingResource resource) {
|
||||
String deviceName = cmd.getDeviceName();
|
||||
int nbdPort = cmd.getNbdPort();
|
||||
|
||||
try {
|
||||
// POC: ImageIO interaction is stubbed out
|
||||
// In production, this would:
|
||||
// 1. Register NBD endpoint nbd://127.0.0.1:{nbdPort}/{deviceName} with ImageIO
|
||||
// 2. Create transfer object in ImageIO
|
||||
// 3. Get signed ticket and transfer URL
|
||||
|
||||
// For POC, return stub data
|
||||
String imageTransferId = "transfer-" + cmd.getDiskId();
|
||||
String transferUrl = String.format("nbd://127.0.0.1:%d/%s", nbdPort, deviceName);
|
||||
String phase = "initializing";
|
||||
|
||||
return new CreateImageTransferAnswer(cmd, true, "Image transfer created (stub)",
|
||||
imageTransferId, transferUrl, phase);
|
||||
|
||||
} catch (Exception e) {
|
||||
return new CreateImageTransferAnswer(cmd, false, "Error creating image transfer: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
//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 com.cloud.hypervisor.kvm.resource.wrapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.backup.StartBackupAnswer;
|
||||
import org.apache.cloudstack.backup.StartBackupCommand;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.libvirt.Connect;
|
||||
import org.libvirt.Domain;
|
||||
import org.libvirt.DomainInfo;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.utils.script.Script;
|
||||
|
||||
@ResourceWrapper(handles = StartBackupCommand.class)
|
||||
public class LibvirtStartBackupCommandWrapper extends CommandWrapper<StartBackupCommand, Answer, LibvirtComputingResource> {
|
||||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
public Answer execute(StartBackupCommand cmd, LibvirtComputingResource resource) {
|
||||
String vmName = cmd.getVmName();
|
||||
String toCheckpointId = cmd.getToCheckpointId();
|
||||
String fromCheckpointId = cmd.getFromCheckpointId();
|
||||
int nbdPort = cmd.getNbdPort();
|
||||
|
||||
try {
|
||||
Connect conn = LibvirtConnection.getConnection();
|
||||
Domain dm = conn.domainLookupByName(vmName);
|
||||
|
||||
if (dm == null) {
|
||||
return new StartBackupAnswer(cmd, false, "Domain not found: " + vmName);
|
||||
}
|
||||
|
||||
DomainInfo info = dm.getInfo();
|
||||
if (info.state != DomainInfo.DomainState.VIR_DOMAIN_RUNNING) {
|
||||
return new StartBackupAnswer(cmd, false, "VM is not running");
|
||||
}
|
||||
|
||||
// Create backup XML
|
||||
String backupXml = createBackupXml(cmd, fromCheckpointId, nbdPort);
|
||||
String checkpointXml = createCheckpointXml(toCheckpointId);
|
||||
|
||||
// Write XMLs to temp files
|
||||
File backupXmlFile = File.createTempFile("backup-", ".xml");
|
||||
File checkpointXmlFile = File.createTempFile("checkpoint-", ".xml");
|
||||
|
||||
try (FileWriter writer = new FileWriter(backupXmlFile)) {
|
||||
writer.write(backupXml);
|
||||
}
|
||||
try (FileWriter writer = new FileWriter(checkpointXmlFile)) {
|
||||
writer.write(checkpointXml);
|
||||
}
|
||||
|
||||
// Execute virsh backup-begin
|
||||
String backupCmd = String.format("virsh backup-begin %s %s --checkpointxml %s",
|
||||
vmName, backupXmlFile.getAbsolutePath(), checkpointXmlFile.getAbsolutePath());
|
||||
|
||||
Script script = new Script("/bin/bash");
|
||||
script.add("-c");
|
||||
script.add(backupCmd);
|
||||
String result = script.execute();
|
||||
|
||||
backupXmlFile.delete();
|
||||
checkpointXmlFile.delete();
|
||||
|
||||
if (result != null) {
|
||||
return new StartBackupAnswer(cmd, false, "Backup begin failed: " + result);
|
||||
}
|
||||
|
||||
// Get checkpoint creation time - using current time for POC
|
||||
long checkpointCreateTime = System.currentTimeMillis();
|
||||
|
||||
// Build device mappings from domblklist
|
||||
Map<Long, String> deviceMappings = getDeviceMappings(vmName, cmd.getDiskVolumePaths(), resource);
|
||||
|
||||
return new StartBackupAnswer(cmd, true, "Backup started successfully",
|
||||
checkpointCreateTime, deviceMappings);
|
||||
|
||||
} catch (Exception e) {
|
||||
return new StartBackupAnswer(cmd, false, "Error starting backup: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String createBackupXml(StartBackupCommand cmd, String fromCheckpointId, int nbdPort) {
|
||||
StringBuilder xml = new StringBuilder();
|
||||
xml.append("<domainbackup mode=\"pull\">\n");
|
||||
|
||||
if (fromCheckpointId != null && !fromCheckpointId.isEmpty()) {
|
||||
xml.append(" <incremental>").append(fromCheckpointId).append("</incremental>\n");
|
||||
}
|
||||
|
||||
xml.append(" <server transport=\"tcp\" name=\"127.0.0.1\" port=\"").append(nbdPort).append("\"/>\n");
|
||||
xml.append(" <disks>\n");
|
||||
|
||||
// Add disk entries - simplified for POC
|
||||
Map<Long, String> diskPaths = cmd.getDiskVolumePaths();
|
||||
int diskIndex = 0;
|
||||
for (Map.Entry<Long, String> entry : diskPaths.entrySet()) {
|
||||
String deviceName = "vd" + (char)('a' + diskIndex);
|
||||
String scratchFile = "/var/tmp/scratch-" + entry.getKey() + ".qcow2";
|
||||
xml.append(" <disk name=\"").append(deviceName).append("\" type=\"file\" exportname=\"")
|
||||
.append(deviceName).append("\">\n");
|
||||
xml.append(" <scratch file=\"").append(scratchFile).append("\"/>\n");
|
||||
xml.append(" </disk>\n");
|
||||
diskIndex++;
|
||||
}
|
||||
|
||||
xml.append(" </disks>\n");
|
||||
xml.append("</domainbackup>");
|
||||
|
||||
return xml.toString();
|
||||
}
|
||||
|
||||
private String createCheckpointXml(String checkpointId) {
|
||||
return "<domaincheckpoint>\n" +
|
||||
" <name>" + checkpointId + "</name>\n" +
|
||||
"</domaincheckpoint>";
|
||||
}
|
||||
|
||||
private Map<Long, String> getDeviceMappings(String vmName, Map<Long, String> diskPaths,
|
||||
LibvirtComputingResource resource) {
|
||||
Map<Long, String> mappings = new HashMap<>();
|
||||
|
||||
// Simplified for POC - map volumeIds to device names in order
|
||||
int diskIndex = 0;
|
||||
for (Long volumeId : diskPaths.keySet()) {
|
||||
String deviceName = "vd" + (char)('a' + diskIndex);
|
||||
mappings.put(volumeId, deviceName);
|
||||
diskIndex++;
|
||||
}
|
||||
|
||||
return mappings;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
//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.hypervisor.kvm.resource.wrapper;
|
||||
|
||||
import org.apache.cloudstack.backup.StopBackupAnswer;
|
||||
import org.apache.cloudstack.backup.StopBackupCommand;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.libvirt.Connect;
|
||||
import org.libvirt.Domain;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
|
||||
import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.utils.script.Script;
|
||||
|
||||
@ResourceWrapper(handles = StopBackupCommand.class)
|
||||
public class LibvirtStopBackupCommandWrapper extends CommandWrapper<StopBackupCommand, Answer, LibvirtComputingResource> {
|
||||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
|
||||
@Override
|
||||
public Answer execute(StopBackupCommand cmd, LibvirtComputingResource resource) {
|
||||
String vmName = cmd.getVmName();
|
||||
|
||||
try {
|
||||
Connect conn = LibvirtConnection.getConnection();
|
||||
Domain dm = conn.domainLookupByName(vmName);
|
||||
|
||||
if (dm == null) {
|
||||
return new StopBackupAnswer(cmd, false, "Domain not found: " + vmName);
|
||||
}
|
||||
|
||||
// Execute virsh domjobabort
|
||||
String abortCmd = String.format("virsh domjobabort %s", vmName);
|
||||
|
||||
Script script = new Script("/bin/bash");
|
||||
script.add("-c");
|
||||
script.add(abortCmd);
|
||||
String result = script.execute();
|
||||
|
||||
if (result != null && !result.isEmpty()) {
|
||||
// Job abort may fail if no job is running, which is acceptable
|
||||
logger.debug("domjobabort result: " + result);
|
||||
}
|
||||
|
||||
return new StopBackupAnswer(cmd, true, "Backup stopped successfully");
|
||||
|
||||
} catch (Exception e) {
|
||||
return new StopBackupAnswer(cmd, false, "Error stopping backup: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2430,6 +2430,13 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
|
|||
response.setVmDetails(vmDetails);
|
||||
}
|
||||
|
||||
if (backup.getFromCheckpointId() != null) {
|
||||
response.setFromCheckpointId(backup.getFromCheckpointId());
|
||||
}
|
||||
if (backup.getToCheckpointId() != null) {
|
||||
response.setToCheckpointId(backup.getToCheckpointId());
|
||||
}
|
||||
|
||||
response.setObjectName("backup");
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,456 @@
|
|||
//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.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.api.command.admin.backup.CreateImageTransferCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.DeleteVmCheckpointCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.FinalizeBackupCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.FinalizeImageTransferCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.ListImageTransfersCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.ListVmCheckpointsCmd;
|
||||
import org.apache.cloudstack.api.command.admin.backup.StartBackupCmd;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.CheckpointResponse;
|
||||
import org.apache.cloudstack.api.response.ImageTransferResponse;
|
||||
import org.apache.cloudstack.backup.dao.BackupDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
|
||||
import org.apache.cloudstack.backup.dao.ImageTransferDao;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.exception.AgentUnavailableException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.VirtualMachine.State;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
|
||||
@Component
|
||||
public class IncrementalBackupServiceImpl extends ManagerBase implements IncrementalBackupService {
|
||||
|
||||
@Inject
|
||||
private VMInstanceDao vmInstanceDao;
|
||||
|
||||
@Inject
|
||||
private BackupDao backupDao;
|
||||
|
||||
@Inject
|
||||
private ImageTransferDao imageTransferDao;
|
||||
|
||||
@Inject
|
||||
private VolumeDao volumeDao;
|
||||
|
||||
@Inject
|
||||
private AgentManager agentManager;
|
||||
|
||||
@Inject
|
||||
private BackupOfferingDao backupOfferingDao;
|
||||
|
||||
private static final int NBD_PORT_RANGE_START = 10809;
|
||||
private static final int NBD_PORT_RANGE_END = 10909;
|
||||
|
||||
private boolean isDummyOffering(Long backupOfferingId) {
|
||||
if (backupOfferingId == null) {
|
||||
throw new CloudRuntimeException("VM not assigned a backup offering");
|
||||
}
|
||||
BackupOfferingVO offering = backupOfferingDao.findById(backupOfferingId);
|
||||
if (offering == null) {
|
||||
throw new CloudRuntimeException("Backup offering not found: " + backupOfferingId);
|
||||
}
|
||||
if ("dummy".equalsIgnoreCase(offering.getName())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackupResponse startBackup(StartBackupCmd cmd) {
|
||||
Long vmId = cmd.getVmId();
|
||||
|
||||
// Get VM
|
||||
VMInstanceVO vm = vmInstanceDao.findById(vmId);
|
||||
if (vm == null) {
|
||||
throw new CloudRuntimeException("VM not found: " + vmId);
|
||||
}
|
||||
|
||||
if (vm.getState() != State.Running) {
|
||||
throw new CloudRuntimeException("VM must be running to start backup");
|
||||
}
|
||||
|
||||
// Check if backup already in progress
|
||||
Backup existingBackup = backupDao.findByVmId(vmId);
|
||||
if (existingBackup != null && existingBackup.getStatus() == Backup.Status.BackingUp) {
|
||||
throw new CloudRuntimeException("Backup already in progress for VM: " + vmId);
|
||||
}
|
||||
|
||||
boolean dummyOffering = isDummyOffering(vm.getBackupOfferingId());
|
||||
|
||||
// Create backup record
|
||||
BackupVO backup = new BackupVO();
|
||||
backup.setVmId(vmId);
|
||||
backup.setName(vmId + "-" + DateTime.now());
|
||||
backup.setAccountId(vm.getAccountId());
|
||||
backup.setDomainId(vm.getDomainId());
|
||||
// todo: set to Increment if it is incremental backup
|
||||
backup.setType("FULL");
|
||||
backup.setZoneId(vm.getDataCenterId());
|
||||
backup.setStatus(Backup.Status.BackingUp);
|
||||
backup.setBackupOfferingId(vm.getBackupOfferingId());
|
||||
backup.setDate(new Date());
|
||||
|
||||
// Generate checkpoint IDs
|
||||
String toCheckpointId = "ckp-" + UUID.randomUUID().toString().substring(0, 8);
|
||||
String fromCheckpointId = vm.getActiveCheckpointId(); // null for first full backup
|
||||
|
||||
backup.setToCheckpointId(toCheckpointId);
|
||||
backup.setFromCheckpointId(fromCheckpointId);
|
||||
|
||||
// Allocate NBD port
|
||||
int nbdPort = allocateNbdPort();
|
||||
backup.setNbdPort(nbdPort);
|
||||
backup.setHostId(vm.getHostId());
|
||||
|
||||
// Persist backup record
|
||||
backup = backupDao.persist(backup);
|
||||
|
||||
// Get disk volume paths
|
||||
List<? extends Volume> volumes = volumeDao.findByInstance(vmId);
|
||||
Map<Long, String> diskVolumePaths = new HashMap<>();
|
||||
for (Volume vol : volumes) {
|
||||
diskVolumePaths.put(vol.getId(), vol.getPath());
|
||||
}
|
||||
|
||||
// Send StartBackupCommand to agent
|
||||
StartBackupCommand startCmd = new StartBackupCommand(
|
||||
vm.getInstanceName(),
|
||||
vmId,
|
||||
toCheckpointId,
|
||||
fromCheckpointId,
|
||||
nbdPort,
|
||||
diskVolumePaths
|
||||
);
|
||||
|
||||
try {
|
||||
StartBackupAnswer answer;
|
||||
|
||||
if (dummyOffering) {
|
||||
answer = new StartBackupAnswer(startCmd, true, "Dummy answer", System.currentTimeMillis(), diskVolumePaths);
|
||||
} else {
|
||||
answer = (StartBackupAnswer) agentManager.send(vm.getHostId(), startCmd);
|
||||
}
|
||||
|
||||
if (!answer.getResult()) {
|
||||
backupDao.remove(backup.getId());
|
||||
throw new CloudRuntimeException("Failed to start backup: " + answer.getDetails());
|
||||
}
|
||||
|
||||
// Update backup with checkpoint creation time
|
||||
backup.setCheckpointCreateTime(answer.getCheckpointCreateTime());
|
||||
backupDao.update(backup.getId(), backup);
|
||||
|
||||
// Return response
|
||||
BackupResponse response = new BackupResponse();
|
||||
response.setId(backup.getUuid());
|
||||
response.setVmId(vm.getUuid());
|
||||
response.setStatus(backup.getStatus());
|
||||
return response;
|
||||
|
||||
} catch (AgentUnavailableException | OperationTimedoutException e) {
|
||||
backupDao.remove(backup.getId());
|
||||
throw new CloudRuntimeException("Failed to communicate with agent: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finalizeBackup(FinalizeBackupCmd cmd) {
|
||||
Long vmId = cmd.getVmId();
|
||||
Long backupId = cmd.getBackupId();
|
||||
|
||||
// Get backup
|
||||
BackupVO backup = backupDao.findById(backupId);
|
||||
if (backup == null) {
|
||||
throw new CloudRuntimeException("Backup not found: " + backupId);
|
||||
}
|
||||
|
||||
if (!backup.getVmId().equals(vmId)) {
|
||||
throw new CloudRuntimeException("Backup does not belong to VM: " + vmId);
|
||||
}
|
||||
|
||||
// Get VM
|
||||
VMInstanceVO vm = vmInstanceDao.findById(vmId);
|
||||
if (vm == null) {
|
||||
throw new CloudRuntimeException("VM not found: " + vmId);
|
||||
}
|
||||
|
||||
boolean dummyOffering = isDummyOffering(vm.getBackupOfferingId());
|
||||
|
||||
List<ImageTransferVO> transfers = imageTransferDao.listByBackupId(backupId);
|
||||
if (CollectionUtils.isNotEmpty(transfers)) {
|
||||
throw new CloudRuntimeException("Image transfers not finalized for backup: " + backupId);
|
||||
}
|
||||
|
||||
// Send StopBackupCommand to agent
|
||||
StopBackupCommand stopCmd = new StopBackupCommand(vm.getInstanceName(), vmId, backupId);
|
||||
|
||||
try {
|
||||
StopBackupAnswer answer;
|
||||
if (dummyOffering) {
|
||||
answer = new StopBackupAnswer(stopCmd, true, "Dummy answer");
|
||||
} else {
|
||||
answer = (StopBackupAnswer) agentManager.send(vm.getHostId(), stopCmd);
|
||||
}
|
||||
|
||||
if (!answer.getResult()) {
|
||||
throw new CloudRuntimeException("Failed to stop backup: " + answer.getDetails());
|
||||
}
|
||||
|
||||
// Update VM checkpoint tracking
|
||||
String oldCheckpointId = vm.getActiveCheckpointId();
|
||||
vm.setActiveCheckpointId(backup.getToCheckpointId());
|
||||
vm.setActiveCheckpointCreateTime(backup.getCheckpointCreateTime());
|
||||
vmInstanceDao.update(vmId, vm);
|
||||
|
||||
// Delete old checkpoint if exists (POC: skip actual libvirt call)
|
||||
if (oldCheckpointId != null) {
|
||||
// In production: send command to delete oldCheckpointId via virsh checkpoint-delete
|
||||
logger.debug("Would delete old checkpoint: " + oldCheckpointId);
|
||||
}
|
||||
|
||||
// Delete backup session record
|
||||
backupDao.remove(backup.getId());
|
||||
|
||||
return true;
|
||||
|
||||
} catch (AgentUnavailableException | OperationTimedoutException e) {
|
||||
throw new CloudRuntimeException("Failed to communicate with agent: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageTransferResponse createImageTransfer(CreateImageTransferCmd cmd) {
|
||||
Long backupId = cmd.getBackupId();
|
||||
Long volumeId = cmd.getVolumeId();
|
||||
|
||||
BackupVO backup = backupDao.findById(backupId);
|
||||
if (backup == null) {
|
||||
throw new CloudRuntimeException("Backup not found: " + backupId);
|
||||
}
|
||||
|
||||
Volume volume = volumeDao.findById(volumeId);
|
||||
if (volume == null) {
|
||||
throw new CloudRuntimeException("Volume not found: " + volumeId);
|
||||
}
|
||||
|
||||
VMInstanceVO vm = vmInstanceDao.findById(backup.getVmId());
|
||||
if (vm == null) {
|
||||
throw new CloudRuntimeException("VM not found: " + backup.getVmId());
|
||||
}
|
||||
boolean dummyOffering = isDummyOffering(vm.getBackupOfferingId());
|
||||
|
||||
// Resolve device name (simplified for POC)
|
||||
List<? extends Volume> volumes = volumeDao.findByInstance(backup.getVmId());
|
||||
String deviceName = resolveDeviceName(volumes, volumeId);
|
||||
|
||||
// Create CreateImageTransferCommand
|
||||
CreateImageTransferCommand transferCmd = new CreateImageTransferCommand(
|
||||
backup.getVmId(),
|
||||
backupId,
|
||||
volumeId,
|
||||
deviceName,
|
||||
backup.getNbdPort()
|
||||
);
|
||||
|
||||
try {
|
||||
CreateImageTransferAnswer answer;
|
||||
if (dummyOffering) {
|
||||
answer = new CreateImageTransferAnswer(transferCmd, true, "Dummy answer", "image-transfer-id", "nbd://127.0.0.1:10809/vda", "initializing");
|
||||
} else {
|
||||
answer = (CreateImageTransferAnswer) agentManager.send(backup.getHostId(), transferCmd);
|
||||
}
|
||||
|
||||
if (!answer.getResult()) {
|
||||
throw new CloudRuntimeException("Failed to create image transfer: " + answer.getDetails());
|
||||
}
|
||||
|
||||
// Create ImageTransfer record
|
||||
ImageTransferVO imageTransfer = new ImageTransferVO(
|
||||
backupId,
|
||||
backup.getVmId(),
|
||||
volumeId,
|
||||
deviceName,
|
||||
backup.getHostId(),
|
||||
backup.getNbdPort(),
|
||||
ImageTransferVO.Phase.initializing,
|
||||
ImageTransfer.Direction.valueOf(cmd.getDirection()),
|
||||
backup.getAccountId(),
|
||||
backup.getDomainId()
|
||||
);
|
||||
imageTransfer.setTransferUrl(answer.getTransferUrl());
|
||||
imageTransfer.setSignedTicketId(answer.getImageTransferId());
|
||||
imageTransfer = imageTransferDao.persist(imageTransfer);
|
||||
|
||||
// Return response
|
||||
ImageTransferResponse response = new ImageTransferResponse();
|
||||
response.setId(imageTransfer.getUuid());
|
||||
response.setBackupId(backup.getUuid());
|
||||
response.setVmId(vm.getUuid());
|
||||
response.setDiskId(volume.getUuid());
|
||||
response.setDeviceName(deviceName);
|
||||
response.setTransferUrl(answer.getTransferUrl());
|
||||
response.setPhase(ImageTransferVO.Phase.initializing.toString());
|
||||
response.setDirection(imageTransfer.getDirection().toString());
|
||||
response.setCreated(imageTransfer.getCreated());
|
||||
return response;
|
||||
|
||||
} catch (AgentUnavailableException | OperationTimedoutException e) {
|
||||
throw new CloudRuntimeException("Failed to communicate with agent: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean finalizeImageTransfer(FinalizeImageTransferCmd cmd) {
|
||||
Long imageTransferId = cmd.getImageTransferId();
|
||||
|
||||
ImageTransferVO imageTransfer = imageTransferDao.findById(imageTransferId);
|
||||
if (imageTransfer == null) {
|
||||
throw new CloudRuntimeException("Image transfer not found: " + imageTransferId);
|
||||
}
|
||||
|
||||
// Mark as finished (NBD is closed in backup finalize, not here)
|
||||
imageTransfer.setPhase(ImageTransferVO.Phase.finished);
|
||||
imageTransferDao.update(imageTransferId, imageTransfer);
|
||||
imageTransferDao.remove(imageTransferId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ImageTransferResponse> listImageTransfers(ListImageTransfersCmd cmd) {
|
||||
Long id = cmd.getId();
|
||||
Long backupId = cmd.getBackupId();
|
||||
|
||||
List<ImageTransferVO> transfers;
|
||||
if (id != null) {
|
||||
transfers = List.of(imageTransferDao.findById(id));
|
||||
} else if (backupId != null) {
|
||||
transfers = imageTransferDao.listByBackupId(backupId);
|
||||
} else {
|
||||
transfers = imageTransferDao.listAll();
|
||||
}
|
||||
|
||||
return transfers.stream().map(this::toImageTransferResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CheckpointResponse> listVmCheckpoints(ListVmCheckpointsCmd cmd) {
|
||||
Long vmId = cmd.getVmId();
|
||||
|
||||
VMInstanceVO vm = vmInstanceDao.findById(vmId);
|
||||
if (vm == null) {
|
||||
throw new CloudRuntimeException("VM not found: " + vmId);
|
||||
}
|
||||
|
||||
// Return active checkpoint (POC: simplified, no libvirt query)
|
||||
List<CheckpointResponse> responses = new ArrayList<>();
|
||||
if (vm.getActiveCheckpointId() != null) {
|
||||
CheckpointResponse response = new CheckpointResponse();
|
||||
response.setCheckpointId(vm.getActiveCheckpointId());
|
||||
response.setCreateTime(vm.getActiveCheckpointCreateTime());
|
||||
response.setIsActive(true);
|
||||
responses.add(response);
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteVmCheckpoint(DeleteVmCheckpointCmd cmd) {
|
||||
// No-op for normal flow as per spec
|
||||
// Kept for API parity with oVirt
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getCommands() {
|
||||
List<Class<?>> cmdList = new ArrayList<>();
|
||||
cmdList.add(StartBackupCmd.class);
|
||||
cmdList.add(FinalizeBackupCmd.class);
|
||||
cmdList.add(CreateImageTransferCmd.class);
|
||||
cmdList.add(FinalizeImageTransferCmd.class);
|
||||
cmdList.add(ListImageTransfersCmd.class);
|
||||
cmdList.add(ListVmCheckpointsCmd.class);
|
||||
cmdList.add(DeleteVmCheckpointCmd.class);
|
||||
return cmdList;
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
private int allocateNbdPort() {
|
||||
// Simplified port allocation for POC
|
||||
Random random = new Random();
|
||||
return NBD_PORT_RANGE_START + random.nextInt(NBD_PORT_RANGE_END - NBD_PORT_RANGE_START);
|
||||
}
|
||||
|
||||
private String resolveDeviceName(List<? extends Volume> volumes, Long targetDiskId) {
|
||||
// Simplified device name resolution for POC
|
||||
int index = 0;
|
||||
for (Volume vol : volumes) {
|
||||
if (Long.valueOf(vol.getId()).equals(targetDiskId)) {
|
||||
return "vd" + (char)('a' + index);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return "vda"; // fallback
|
||||
}
|
||||
|
||||
private ImageTransferResponse toImageTransferResponse(ImageTransferVO imageTransfer) {
|
||||
ImageTransferResponse response = new ImageTransferResponse();
|
||||
response.setId(imageTransfer.getUuid());
|
||||
|
||||
BackupVO backup = backupDao.findById(imageTransfer.getBackupId());
|
||||
VMInstanceVO vm = vmInstanceDao.findById(imageTransfer.getVmId());
|
||||
Volume volume = volumeDao.findById(imageTransfer.getDiskId());
|
||||
|
||||
if (backup != null) response.setBackupId(backup.getUuid());
|
||||
if (vm != null) response.setVmId(vm.getUuid());
|
||||
if (volume != null) response.setDiskId(volume.getUuid());
|
||||
|
||||
response.setDeviceName(imageTransfer.getDeviceName());
|
||||
response.setTransferUrl(imageTransfer.getTransferUrl());
|
||||
response.setPhase(imageTransfer.getPhase().toString());
|
||||
response.setCreated(imageTransfer.getCreated());
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
@ -347,6 +347,8 @@
|
|||
|
||||
<bean id="backupRepositoryService" class="org.apache.cloudstack.backup.BackupRepositoryServiceImpl" />
|
||||
|
||||
<bean id="incrementalBackupService" class="org.apache.cloudstack.backup.IncrementalBackupServiceImpl" />
|
||||
|
||||
<bean id="storageLayer" class="com.cloud.storage.JavaStorageLayer" />
|
||||
|
||||
<bean id="nfsMountManager" class="org.apache.cloudstack.storage.NfsMountManagerImpl" >
|
||||
|
|
|
|||
|
|
@ -223,6 +223,15 @@ known_categories = {
|
|||
'Management': 'Management',
|
||||
'Backup' : 'Backup and Recovery',
|
||||
'Restore' : 'Backup and Recovery',
|
||||
'startBackup' : 'Backup and Recovery',
|
||||
'finalizeBackup' : 'Backup and Recovery',
|
||||
'createImageTransfer' : 'Backup and Recovery',
|
||||
'finalizeImageTransfer' : 'Backup and Recovery',
|
||||
'listImageTransfers' : 'Backup and Recovery',
|
||||
'listVmCheckpoints' : 'Backup and Recovery',
|
||||
'deleteVmCheckpoint' : 'Backup and Recovery',
|
||||
'ImageTransfer' : 'Backup and Recovery',
|
||||
'VmCheckpoint' : 'Backup and Recovery',
|
||||
'UnmanagedInstance': 'Virtual Machine',
|
||||
'KubernetesSupportedVersion': 'Kubernetes Service',
|
||||
'KubernetesCluster': 'Kubernetes Service',
|
||||
|
|
|
|||
Loading…
Reference in New Issue