changes for user assignement; refactor

- make service account configurable
- allow assigning vm, volume to network account

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
Abhishek Kumar 2026-03-11 12:32:05 +05:30
parent a0be1fb772
commit 05a5b03d95
22 changed files with 463 additions and 192 deletions

View File

@ -88,10 +88,14 @@ public interface AccountService {
Account getActiveAccountById(long accountId);
Account getActiveAccountByUuid(String accountUuid);
Account getAccount(long accountId);
User getActiveUser(long userId);
User getOneActiveUserForAccount(Account account);
User getUserIncludingRemoved(long userId);
boolean isRootAdmin(Long accountId);

View File

@ -85,6 +85,8 @@ public class AssignVMCmd extends BaseCmd {
"In case no security groups are provided the Instance is part of the default security group.")
private List<Long> securityGroupIdList;
private boolean skipNetwork = false;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -113,6 +115,34 @@ public class AssignVMCmd extends BaseCmd {
return securityGroupIdList;
}
public boolean isSkipNetwork() {
return skipNetwork;
}
/////////////////////////////////////////////////////
/////////////////// Setters /////////////////////////
/////////////////////////////////////////////////////
public void setVirtualMachineId(Long virtualMachineId) {
this.virtualMachineId = virtualMachineId;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public void setDomainId(Long domainId) {
this.domainId = domainId;
}
public void setProjectId(Long projectId) {
this.projectId = projectId;
}
public void setSkipNetwork(boolean skipNetwork) {
this.skipNetwork = skipNetwork;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -70,6 +70,21 @@ public class AssignVolumeCmd extends BaseCmd implements UserCmd {
return projectid;
}
/////////////////////////////////////////////////////
/////////////////// Setter///////////////////////////
/////////////////////////////////////////////////////
public void setVolumeId(Long volumeId) {
this.volumeId = volumeId;
}
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
public void setProjectId(Long projectid) {
this.projectid = projectid;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -50,4 +50,6 @@ public interface AsyncJobDao extends GenericDao<AsyncJobVO, Long> {
// Returns the number of pending jobs for the given Management server msids.
// NOTE: This is the msid and NOT the id
long countPendingNonPseudoJobs(Long... msIds);
List<Long> listPendingJobIdsForAccount(long accountId);
}

View File

@ -266,4 +266,14 @@ public class AsyncJobDaoImpl extends GenericDaoBase<AsyncJobVO, Long> implements
List<Long> results = customSearch(sc, null);
return results.get(0);
}
@Override
public List<Long> listPendingJobIdsForAccount(long accountId) {
GenericSearchBuilder<AsyncJobVO, Long> sb = createSearchBuilder(Long.class);
sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ);
sb.selectFields(sb.entity().getId());
SearchCriteria<Long> sc = sb.create();
sc.setParameters("accountId", accountId);
return customSearch(sc, null);
}
}

View File

@ -88,8 +88,8 @@ public class LibvirtStartBackupCommandWrapper extends CommandWrapper<StartBackup
script.add(backupCmd);
String result = script.execute();
backupXmlFile.delete();
checkpointXmlFile.delete();
// backupXmlFile.delete();
// checkpointXmlFile.delete();
if (result != null) {
return new StartBackupAnswer(cmd, false, "Backup begin failed: " + result);

View File

@ -31,8 +31,19 @@ public interface VeeamControlService extends PluggableService, Configurable {
"8090", "Port for Veeam Integration REST API server", false);
ConfigKey<String> ContextPath = new ConfigKey<>("Advanced", String.class, "integration.veeam.control.context.path",
"/ovirt-engine", "Context path for Veeam Integration REST API server", false);
ConfigKey<String> Username = new ConfigKey<>("Advanced", String.class, "integration.veeam.api.username",
ConfigKey<String> Username = new ConfigKey<>("Advanced", String.class, "integration.veeam.control.api.username",
"veeam", "Username for Basic Auth on Veeam Integration REST API server", true);
ConfigKey<String> Password = new ConfigKey<>("Advanced", String.class, "integration.veeam.api.password",
ConfigKey<String> Password = new ConfigKey<>("Advanced", String.class, "integration.veeam.control.api.password",
"change-me", "Password for Basic Auth on Veeam Integration REST API server", true);
ConfigKey<String> ServiceAccountId = new ConfigKey<>("Advanced", String.class,
"integration.veeam.control.service.account", "",
"ID of the service account used to perform operations on resources. " +
"Preferably an admin-level account with permissions to access resources across the environment " +
"and optionally assign them to other users.",
true);
ConfigKey<Boolean> InstanceRestoreAssignOwner = new ConfigKey<>("Advanced", Boolean.class,
"integration.veeam.instance.restore.assign.owner",
"false", "Attempt to assign restored Instance to the owner based on OVF and network " +
"details. If the assignment fails or set to false then the Instance will remain owned by the service " +
"account", true);
}

View File

@ -76,7 +76,9 @@ public class VeeamControlServiceImpl extends ManagerBase implements VeeamControl
Port,
ContextPath,
Username,
Password
Password,
ServiceAccountId,
InstanceRestoreAssignOwner
};
}
}

View File

@ -42,6 +42,7 @@ import org.apache.cloudstack.api.BaseCmd;
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.StartBackupCmd;
import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd;
import org.apache.cloudstack.api.command.admin.vm.DeployVMCmdByAdmin;
import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd;
import org.apache.cloudstack.api.command.user.network.ListNetworksCmd;
@ -54,6 +55,8 @@ import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
import org.apache.cloudstack.api.command.user.vm.StopVMCmd;
import org.apache.cloudstack.api.command.user.vmsnapshot.CreateVMSnapshotCmd;
import org.apache.cloudstack.api.command.user.vmsnapshot.DeleteVMSnapshotCmd;
import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToVMSnapshotCmd;
import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.DeleteVolumeCmd;
@ -72,7 +75,6 @@ import org.apache.cloudstack.backup.dao.ImageTransferDao;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao;
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
import org.apache.cloudstack.jobs.JobInfo;
import org.apache.cloudstack.query.QueryService;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
@ -116,7 +118,6 @@ import org.apache.cloudstack.veeam.api.dto.VnicProfile;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.cloud.api.query.dao.AsyncJobJoinDao;
import com.cloud.api.query.dao.DataCenterJoinDao;
@ -138,9 +139,11 @@ import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.network.NetworkModel;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.offering.ServiceOffering;
@ -173,6 +176,9 @@ import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.snapshot.VMSnapshotVO;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
// ToDo: fix list APIs to support pagination, etc
// ToDo: check access on objects
public class ServerAdapter extends ManagerBase {
private static final String SERVICE_ACCOUNT_NAME = "veemserviceuser";
private static final String SERVICE_ACCOUNT_ROLE_NAME = "Veeam Service Role";
@ -279,7 +285,8 @@ public class ServerAdapter extends ManagerBase {
@Inject
ResourceTagDao resourceTagDao;
//ToDo: check access on objects
@Inject
NetworkModel networkModel;
protected static Tag getDummyTagByName(String name) {
Tag tag = new Tag();
@ -340,7 +347,7 @@ public class ServerAdapter extends ManagerBase {
}
}
protected Pair<User, Account> createServiceAccountIfNeeded() {
protected Pair<User, Account> getDefaultServiceAccount() {
UserAccount userAccount = accountService.getActiveUserAccount(SERVICE_ACCOUNT_NAME, 1L);
if (userAccount == null) {
userAccount = createServiceAccount();
@ -351,9 +358,64 @@ public class ServerAdapter extends ManagerBase {
accountService.getActiveAccountById(userAccount.getAccountId()));
}
protected Pair<User, Account> getServiceAccount() {
String serviceAccountUuid = VeeamControlService.ServiceAccountId.value();
if (StringUtils.isEmpty(serviceAccountUuid)) {
throw new CloudRuntimeException("Service account is not configured, unable to proceed");
}
Account account = accountService.getActiveAccountByUuid(serviceAccountUuid);
if (account == null) {
throw new CloudRuntimeException("Service account with ID " + serviceAccountUuid + " not found, unable to proceed");
}
User user = accountService.getOneActiveUserForAccount(account);
if (user == null) {
throw new CloudRuntimeException("No active user found for service account with ID " + serviceAccountUuid);
}
return new Pair<>(user, account);
}
protected void waitForJobCompletion(long jobId) {
long timeoutNanos = TimeUnit.MINUTES.toNanos(5);
final long deadline = System.nanoTime() + timeoutNanos;
long sleepMillis = 500;
while (true) {
AsyncJobVO job = asyncJobDao.findById(jobId);
if (job == null) {
logger.warn("Async job with ID {} not found", jobId);
return;
}
if (job.getStatus() == AsyncJobVO.Status.SUCCEEDED || job.getStatus() == AsyncJobVO.Status.FAILED) {
return;
}
if (System.nanoTime() > deadline) {
logger.warn("Timed out waiting for {} completion", job);
}
try {
Thread.sleep(sleepMillis);
// back off gradually to reduce DB pressure
sleepMillis = Math.min(5000, sleepMillis + 500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.warn("Interrupted while waiting for async job completion");
}
}
}
protected void waitForJobCompletion(AsyncJobJoinVO job) {
if (job == null) {
logger.warn("Async job not found");
return;
}
if (job.getStatus() == AsyncJobVO.Status.SUCCEEDED.ordinal() ||
job.getStatus() == AsyncJobVO.Status.FAILED.ordinal()) {
logger.warn("Async job with ID {} already completed with status {}", job.getId(), job.getStatus());
}
waitForJobCompletion(job.getId());
}
@Override
public boolean start() {
createServiceAccountIfNeeded();
getServiceAccount();
//find public custom disk offering
return true;
}
@ -445,7 +507,6 @@ public class ServerAdapter extends ManagerBase {
}
public List<Vm> listAllInstances() {
// Todo: add filtering, pagination
List<UserVmJoinVO> vms = userVmJoinDao.listAll();
return UserVmJoinVOToVmConverter.toVmList(vms, this::getHostById);
}
@ -512,7 +573,7 @@ public class ServerAdapter extends ManagerBase {
bootType = ApiConstants.BootType.UEFI;
bootMode = ApiConstants.BootMode.SECURE;
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
return createInstance(zoneId, clusterId, name, displayName, cpu, memory, userdata, bootType, bootMode);
@ -561,7 +622,7 @@ public class ServerAdapter extends ManagerBase {
if (bootMode != null) {
cmd.setBootMode(bootMode.toString());
}
// ToDo: handle other.
// ToDo: handle any other field?
cmd.setHypervisor(Hypervisor.HypervisorType.KVM.name());
cmd.setBlankInstance(true);
Map<String, String> details = new HashMap<>();
@ -581,19 +642,33 @@ public class ServerAdapter extends ManagerBase {
}
public Vm updateInstance(String uuid, Vm request) {
// ToDo: what to do?!
logger.warn("Received request to update VM with ID {}. No action, returning existing VM data.", uuid);
return getInstance(uuid, false, false, false);
}
public void deleteInstance(String uuid) {
public VmAction deleteInstance(String uuid) {
UserVmVO vo = userVmDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
}
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
userVmService.destroyVm(vo.getId(), true);
} catch (ResourceUnavailableException e) {
DestroyVMCmd cmd = new DestroyVMCmd();
cmd.setHttpMethod(BaseCmd.HTTPMethod.POST.name());
ComponentContext.inject(cmd);
Map<String, String> params = new HashMap<>();
params.put(ApiConstants.ID, vo.getUuid());
params.put(ApiConstants.EXPUNGE, Boolean.TRUE.toString());
ApiServerService.AsyncCmdResult result =
apiServerService.processAsyncCmd(cmd, params, ctx, serviceUserAccount.first().getId(),
serviceUserAccount.second());
AsyncJobJoinVO asyncJobJoinVO = asyncJobJoinDao.findById(result.jobId);
return AsyncJobJoinVOToJobConverter.toVmAction(asyncJobJoinVO, userVmJoinDao.findById(vo.getId()));
} catch (Exception e) {
throw new CloudRuntimeException("Failed to delete VM: " + e.getMessage(), e);
} finally {
CallContext.unregister();
}
}
@ -602,7 +677,7 @@ public class ServerAdapter extends ManagerBase {
if (vo == null) {
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
StartVMCmd cmd = new StartVMCmd();
@ -627,7 +702,7 @@ public class ServerAdapter extends ManagerBase {
if (vo == null) {
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
StopVMCmd cmd = new StopVMCmd();
@ -653,7 +728,7 @@ public class ServerAdapter extends ManagerBase {
if (vo == null) {
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
StopVMCmd cmd = new StopVMCmd();
@ -674,9 +749,13 @@ public class ServerAdapter extends ManagerBase {
}
}
protected Long getVolumePhysicalSize(VolumeJoinVO vo) {
return volumeApiService.getVolumePhysicalSize(vo.getFormat(), vo.getPath(), vo.getChainInfo());
}
public List<Disk> listAllDisks() {
List<VolumeJoinVO> kvmVolumes = volumeJoinDao.listByHypervisor(Hypervisor.HypervisorType.KVM);
return VolumeJoinVOToDiskConverter.toDiskList(kvmVolumes);
return VolumeJoinVOToDiskConverter.toDiskList(kvmVolumes, this::getVolumePhysicalSize);
}
public Disk getDisk(String uuid) {
@ -684,7 +763,7 @@ public class ServerAdapter extends ManagerBase {
if (vo == null) {
throw new InvalidParameterValueException("Disk with ID " + uuid + " not found");
}
return VolumeJoinVOToDiskConverter.toDisk(vo);
return VolumeJoinVOToDiskConverter.toDisk(vo, this::getVolumePhysicalSize);
}
public Disk copyDisk(String uuid) {
@ -723,7 +802,7 @@ public class ServerAdapter extends ManagerBase {
protected List<DiskAttachment> listDiskAttachmentsByInstanceId(final long instanceId) {
List<VolumeJoinVO> kvmVolumes = volumeJoinDao.listByInstanceId(instanceId);
return VolumeJoinVOToDiskConverter.toDiskAttachmentList(kvmVolumes);
return VolumeJoinVOToDiskConverter.toDiskAttachmentList(kvmVolumes, this::getVolumePhysicalSize);
}
public List<DiskAttachment> listDiskAttachmentsByInstanceUuid(final String uuid) {
@ -734,7 +813,35 @@ public class ServerAdapter extends ManagerBase {
return listDiskAttachmentsByInstanceId(vo.getId());
}
public DiskAttachment handleInstanceAttachDisk(final String vmUuid, final DiskAttachment request) {
protected void assignVolumeToAccount(VolumeVO volumeVO, long accountId, Pair<User, Account> serviceUserAccount) {
Account account = accountService.getActiveAccountById(accountId);
if (account == null) {
throw new InvalidParameterValueException("Account with ID " + accountId + " not found");
}
CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
AssignVolumeCmd cmd = new AssignVolumeCmd();
ComponentContext.inject(cmd);
Map<String, String> params = new HashMap<>();
cmd.setVolumeId(volumeVO.getId());
params.put(ApiConstants.VOLUME_ID, volumeVO.getUuid());
if (Account.Type.PROJECT.equals(account.getType())) {
cmd.setProjectId(account.getId());
params.put(ApiConstants.PROJECT_ID, account.getUuid());
} else {
cmd.setAccountId(account.getId());
params.put(ApiConstants.ACCOUNT_ID, account.getUuid());
}
cmd.setFullUrlParams(params);
volumeApiService.assignVolumeToAccount(cmd);
} catch (ResourceAllocationException | CloudRuntimeException e) {
logger.error("Failed to assign {} to {}: {}", volumeVO, account, e.getMessage(), e);
} finally {
CallContext.unregister();
}
}
public DiskAttachment attachInstanceDisk(final String vmUuid, final DiskAttachment request) {
UserVmVO vmVo = userVmDao.findByUuid(vmUuid);
if (vmVo == null) {
throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found");
@ -746,12 +853,25 @@ public class ServerAdapter extends ManagerBase {
if (volumeVO == null) {
throw new InvalidParameterValueException("Disk with ID " + request.getDisk().getId() + " not found");
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
if (vmVo.getAccountId() != volumeVO.getAccountId()) {
if (VeeamControlService.InstanceRestoreAssignOwner.value()) {
assignVolumeToAccount(volumeVO, vmVo.getAccountId(), serviceUserAccount);
} else {
throw new PermissionDeniedException("Disk with ID " + request.getDisk().getId() +
" belongs to a different account and cannot be attached to the VM");
}
}
CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
Volume volume = volumeApiService.attachVolumeToVM(vmVo.getId(), volumeVO.getId(), 0L, false);
Long deviceId = null;
List<VolumeVO> volumes = volumeDao.findUsableVolumesForInstance(vmVo.getId());
if (CollectionUtils.isEmpty(volumes)) {
deviceId = 0L;
}
Volume volume = volumeApiService.attachVolumeToVM(vmVo.getId(), volumeVO.getId(), deviceId, false);
VolumeJoinVO attachedVolumeVO = volumeJoinDao.findById(volume.getId());
return VolumeJoinVOToDiskConverter.toDiskAttachment(attachedVolumeVO);
return VolumeJoinVOToDiskConverter.toDiskAttachment(attachedVolumeVO, this::getVolumePhysicalSize);
} finally {
CallContext.unregister();
}
@ -765,7 +885,7 @@ public class ServerAdapter extends ManagerBase {
volumeApiService.deleteVolume(vo.getId(), accountService.getSystemAccount());
}
public Disk handleCreateDisk(Disk request) {
public Disk createDisk(Disk request) {
if (request == null) {
throw new InvalidParameterValueException("Request disk data is empty");
}
@ -805,7 +925,7 @@ public class ServerAdapter extends ManagerBase {
initialSize = Long.parseLong(request.getInitialSize());
} catch (NumberFormatException ignored) {}
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
Account serviceAccount = serviceUserAccount.second();
DataCenterVO zone = dataCenterDao.findById(pool.getDataCenterId());
if (zone == null || !Grouping.AllocationState.Enabled.equals(zone.getAllocationState())) {
@ -815,7 +935,7 @@ public class ServerAdapter extends ManagerBase {
if (diskOfferingId == null) {
throw new CloudRuntimeException("Failed to find custom offering for disk" + zone.getName());
}
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
return createDisk(serviceAccount, pool, name, diskOfferingId, provisionedSizeInGb, initialSize);
} finally {
@ -841,7 +961,7 @@ public class ServerAdapter extends ManagerBase {
}
// Implementation for creating a Disk resource
return VolumeJoinVOToDiskConverter.toDisk(volumeJoinDao.findById(volume.getId()));
return VolumeJoinVOToDiskConverter.toDisk(volumeJoinDao.findById(volume.getId()), this::getVolumePhysicalSize);
}
protected List<Nic> listNicsByInstance(final long instanceId, final String instanceUuid) {
@ -861,7 +981,42 @@ public class ServerAdapter extends ManagerBase {
return listNicsByInstance(vo.getId(), vo.getUuid());
}
public Nic handleAttachInstanceNic(final String vmUuid, final Nic request) {
protected boolean accountCannotAccessNetwork(NetworkVO networkVO, long accountId) {
Account account = accountService.getActiveAccountById(accountId);
try {
networkModel.checkNetworkPermissions(account, networkVO);
return false;
} catch (CloudRuntimeException e) {
logger.debug("{} cannot access {}: {}", account, networkVO, e.getMessage());
}
return true;
}
protected void assignVmToAccount(UserVmVO vmVO, long accountId, Pair<User, Account> serviceUserAccount) {
Account account = accountService.getActiveAccountById(accountId);
if (account == null) {
throw new InvalidParameterValueException("Account with ID " + accountId + " not found");
}
CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
AssignVMCmd cmd = new AssignVMCmd();
ComponentContext.inject(cmd);
cmd.setVirtualMachineId(vmVO.getId());
cmd.setAccountName(account.getAccountName());
cmd.setDomainId(account.getDomainId());
if (Account.Type.PROJECT.equals(account.getType())) {
cmd.setProjectId(account.getId());
}
userVmService.moveVmToUser(cmd);
} catch (ResourceAllocationException | CloudRuntimeException | ResourceUnavailableException |
InsufficientCapacityException e) {
logger.error("Failed to assign {} to {}: {}", vmVO, account, e.getMessage(), e);
} finally {
CallContext.unregister();
}
}
public Nic attachInstanceNic(final String vmUuid, final Nic request) {
UserVmVO vmVo = userVmDao.findByUuid(vmUuid);
if (vmVo == null) {
throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found");
@ -873,7 +1028,13 @@ public class ServerAdapter extends ManagerBase {
if (networkVO == null) {
throw new InvalidParameterValueException("VNic profile " + request.getVnicProfile().getId() + " not found");
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
if (vmVo.getAccountId() != networkVO.getAccountId() &&
networkVO.getAccountId() != Account.ACCOUNT_ID_SYSTEM &&
VeeamControlService.InstanceRestoreAssignOwner.value() &&
accountCannotAccessNetwork(networkVO, vmVo.getAccountId())) {
assignVmToAccount(vmVo, networkVO.getAccountId(), serviceUserAccount);
}
CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
AddNicToVMCmd cmd = new AddNicToVMCmd();
@ -907,7 +1068,7 @@ public class ServerAdapter extends ManagerBase {
return ImageTransferVOToImageTransferConverter.toImageTransfer(vo, this::getHostById, this::getVolumeById);
}
public ImageTransfer handleCreateImageTransfer(ImageTransfer request) {
public ImageTransfer createImageTransfer(ImageTransfer request) {
if (request == null) {
throw new InvalidParameterValueException("Request image transfer data is empty");
}
@ -934,7 +1095,7 @@ public class ServerAdapter extends ManagerBase {
return createImageTransfer(backupId, volumeVO.getId(), direction, format);
}
public boolean handleCancelImageTransfer(String uuid) {
public boolean cancelImageTransfer(String uuid) {
ImageTransferVO vo = imageTransferDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found");
@ -942,7 +1103,7 @@ public class ServerAdapter extends ManagerBase {
return incrementalBackupService.cancelImageTransfer(vo.getId());
}
public boolean handleFinalizeImageTransfer(String uuid) {
public boolean finalizeImageTransfer(String uuid) {
ImageTransferVO vo = imageTransferDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found");
@ -951,7 +1112,7 @@ public class ServerAdapter extends ManagerBase {
}
private ImageTransfer createImageTransfer(Long backupId, Long volumeId, Direction direction, Format format) {
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
org.apache.cloudstack.backup.ImageTransfer imageTransfer =
@ -992,8 +1153,10 @@ public class ServerAdapter extends ManagerBase {
}
public List<Job> listAllJobs() {
// ToDo: find active jobs for service account
return Collections.emptyList();
Pair<User, Account> serviceUserAccount = getServiceAccount();
List<Long> jobIds = asyncJobDao.listPendingJobIdsForAccount(serviceUserAccount.second().getId());
List<AsyncJobJoinVO> jobJoinVOs = asyncJobJoinDao.listByIds(jobIds);
return AsyncJobJoinVOToJobConverter.toJobList(jobJoinVOs);
}
public Job getJob(String uuid) {
@ -1013,12 +1176,12 @@ public class ServerAdapter extends ManagerBase {
return VmSnapshotVOToSnapshotConverter.toSnapshotList(snapshots, vo.getUuid());
}
public Snapshot handleCreateInstanceSnapshot(final String vmUuid, final Snapshot request) {
public Snapshot createInstanceSnapshot(final String vmUuid, final Snapshot request) {
UserVmVO vmVo = userVmDao.findByUuid(vmUuid);
if (vmVo == null) {
throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found");
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
CreateVMSnapshotCmd cmd = new CreateVMSnapshotCmd();
@ -1060,7 +1223,7 @@ public class ServerAdapter extends ManagerBase {
if (vo == null) {
throw new InvalidParameterValueException("Snapshot with ID " + uuid + " not found");
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
DeleteVMSnapshotCmd cmd = new DeleteVMSnapshotCmd();
@ -1074,10 +1237,10 @@ public class ServerAdapter extends ManagerBase {
if (jobVo == null) {
throw new CloudRuntimeException("Failed to find job for snapshot deletion");
}
action = AsyncJobJoinVOToJobConverter.toAction(jobVo);
if (async) {
// ToDo: wait for job completion?
if (!async) {
waitForJobCompletion(jobVo);
}
action = AsyncJobJoinVOToJobConverter.toAction(jobVo);
} catch (Exception e) {
throw new CloudRuntimeException("Failed to delete snapshot: " + e.getMessage(), e);
} finally {
@ -1086,34 +1249,33 @@ public class ServerAdapter extends ManagerBase {
return action;
}
public ResourceAction revertToSnapshot(String uuid) {
throw new InvalidParameterValueException("revertToSnapshot with ID " + uuid + " not implemented");
// ResourceAction action = null;
// VMSnapshotVO vo = vmSnapshotDao.findByUuid(uuid);
// if (vo == null) {
// throw new InvalidParameterValueException("Snapshot with ID " + uuid + " not found");
// }
// Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
// CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
// try {
// RevertToVMSnapshotCmd cmd = new RevertToVMSnapshotCmd();
// ComponentContext.inject(cmd);
// Map<String, String> params = new HashMap<>();
// params.put(ApiConstants.VM_SNAPSHOT_ID, vo.getUuid());
// ApiServerService.AsyncCmdResult result =
// apiServerService.processAsyncCmd(cmd, params, ctx, serviceUserAccount.first().getId(),
// serviceUserAccount.second());
// AsyncJobJoinVO jobVo = asyncJobJoinDao.findById(result.jobId);
// if (jobVo == null) {
// throw new CloudRuntimeException("Failed to find job for snapshot revert");
// }
// action = AsyncJobJoinVOToJobConverter.toAction(jobVo);
// } catch (Exception e) {
// throw new CloudRuntimeException("Failed to revert to snapshot: " + e.getMessage(), e);
// } finally {
// CallContext.unregister();
// }
// return action;
public ResourceAction revertInstanceToSnapshot(String uuid) {
ResourceAction action = null;
VMSnapshotVO vo = vmSnapshotDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Snapshot with ID " + uuid + " not found");
}
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
RevertToVMSnapshotCmd cmd = new RevertToVMSnapshotCmd();
ComponentContext.inject(cmd);
Map<String, String> params = new HashMap<>();
params.put(ApiConstants.VM_SNAPSHOT_ID, vo.getUuid());
ApiServerService.AsyncCmdResult result =
apiServerService.processAsyncCmd(cmd, params, ctx, serviceUserAccount.first().getId(),
serviceUserAccount.second());
AsyncJobJoinVO jobVo = asyncJobJoinDao.findById(result.jobId);
if (jobVo == null) {
throw new CloudRuntimeException("Failed to find job for snapshot revert");
}
action = AsyncJobJoinVOToJobConverter.toAction(jobVo);
} catch (Exception e) {
throw new CloudRuntimeException("Failed to revert to snapshot: " + e.getMessage(), e);
} finally {
CallContext.unregister();
}
return action;
}
public List<Backup> listBackupsByInstanceUuid(final String uuid) {
@ -1130,7 +1292,7 @@ public class ServerAdapter extends ManagerBase {
if (vmVo == null) {
throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found");
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
StartBackupCmd cmd = new StartBackupCmd();
@ -1156,42 +1318,6 @@ public class ServerAdapter extends ManagerBase {
}
}
@Nullable
private BackupVO getBackupFromJob(ApiServerService.AsyncCmdResult result, UserVmVO vmVo) {
AsyncJobVO jobVo = null;
// wait for job to complete and get backup ID
long timeoutNanos = TimeUnit.MINUTES.toNanos(2);
final long deadline = System.nanoTime() + timeoutNanos;
long sleepMillis = 1000;
while (System.nanoTime() < deadline) {
jobVo = asyncJobDao.findByIdIncludingRemoved(result.jobId);
if (jobVo == null) {
throw new CloudRuntimeException("Failed to find job for backup creation");
}
if (!JobInfo.Status.IN_PROGRESS.equals(jobVo.getStatus())) {
break;
}
try {
Thread.sleep(sleepMillis);
// back off gradually to reduce DB pressure
sleepMillis = Math.min(5000, sleepMillis + 500);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new CloudRuntimeException("Interrupted while waiting for backup creation job", ie);
}
}
// if still in progress after timeout, fail fast
if (jobVo != null && JobInfo.Status.IN_PROGRESS.equals(jobVo.getStatus())) {
throw new CloudRuntimeException("Timed out waiting for backup creation job");
}
BackupVO vo = null;
List<BackupVO> backups = backupDao.searchByVmIds(List.of(vmVo.getId()));
if (CollectionUtils.isNotEmpty(backups)) {
vo = backups.get(0);
}
return vo;
}
public Backup getBackup(String uuid) {
BackupVO vo = backupDao.findByUuidIncludingRemoved(uuid);
if (vo == null) {
@ -1203,12 +1329,7 @@ public class ServerAdapter extends ManagerBase {
public List<Disk> listDisksByBackupUuid(final String uuid) {
throw new InvalidParameterValueException("List Backup Disks with ID " + uuid + " not implemented");
// ToDo: implement
// BackupVO vo = backupDao.findByUuid(uuid);
// if (vo == null) {
// throw new InvalidParameterValueException("Backup with ID " + uuid + " not found");
// }
// return VolumeJoinVOToDiskConverter.toDiskList(volumes);
// This won't be feasible with current structure
}
public Backup finalizeBackup(final String vmUuid, final String backupUuid) {
@ -1220,7 +1341,7 @@ public class ServerAdapter extends ManagerBase {
if (backup == null) {
throw new InvalidParameterValueException("Backup with ID " + backupUuid + " not found");
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext ctx = CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
FinalizeBackupCmd cmd = new FinalizeBackupCmd();
@ -1271,7 +1392,7 @@ public class ServerAdapter extends ManagerBase {
logger.warn("Checkpoint ID {} does not match active checkpoint for VM {}", checkpointId, vmUuid);
return;
}
Pair<User, Account> serviceUserAccount = createServiceAccountIfNeeded();
Pair<User, Account> serviceUserAccount = getServiceAccount();
CallContext.register(serviceUserAccount.first(), serviceUserAccount.second());
try {
DeleteVmCheckpointCmd cmd = new DeleteVmCheckpointCmd();

View File

@ -130,7 +130,7 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler {
String data = RouteHandler.getRequestData(req, logger);
try {
Disk request = io.getMapper().jsonMapper().readValue(data, Disk.class);
Disk response = serverAdapter.handleCreateDisk(request);
Disk response = serverAdapter.createDisk(request);
io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat);
} catch (JsonProcessingException | CloudRuntimeException e) {
io.badRequest(resp, e.getMessage(), outFormat);

View File

@ -115,7 +115,7 @@ public class ImageTransfersRouteHandler extends ManagerBase implements RouteHand
String data = RouteHandler.getRequestData(req, logger);
try {
ImageTransfer request = io.getMapper().jsonMapper().readValue(data, ImageTransfer.class);
ImageTransfer response = serverAdapter.handleCreateImageTransfer(request);
ImageTransfer response = serverAdapter.createImageTransfer(request);
io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat);
} catch (JsonProcessingException | CloudRuntimeException e) {
io.badRequest(resp, e.getMessage(), outFormat);
@ -128,27 +128,27 @@ public class ImageTransfersRouteHandler extends ManagerBase implements RouteHand
ImageTransfer response = serverAdapter.getImageTransfer(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.notFound(resp, e.getMessage(), outFormat);
io.badRequest(resp, e.getMessage(), outFormat);
}
}
protected void handleCancelById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
try {
serverAdapter.handleCancelImageTransfer(id);
serverAdapter.cancelImageTransfer(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, "Image transfer cancelled successfully", outFormat);
} catch (InvalidParameterValueException e) {
io.notFound(resp, e.getMessage(), outFormat);
} catch (CloudRuntimeException e) {
io.badRequest(resp, e.getMessage(), outFormat);
}
}
protected void handleFinalizeById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
try {
serverAdapter.handleFinalizeImageTransfer(id);
serverAdapter.finalizeImageTransfer(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, "Image transfer finalized successfully", outFormat);
} catch (CloudRuntimeException e) {
io.notFound(resp, e.getMessage(), outFormat);
io.badRequest(resp, e.getMessage(), outFormat);
}
}
}

View File

@ -345,15 +345,15 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
protected void handleDeleteById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
try {
serverAdapter.deleteInstance(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, "", outFormat);
VmAction vm = serverAdapter.deleteInstance(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, vm, outFormat);
} catch (CloudRuntimeException e) {
io.notFound(resp, e.getMessage(), outFormat);
}
}
protected void handleStartVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
protected void handleStartVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp,
final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException {
try {
VmAction vm = serverAdapter.startInstance(id);
io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat);
@ -362,8 +362,8 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
}
}
protected void handleStopVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
protected void handleStopVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp,
final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException {
try {
VmAction vm = serverAdapter.stopInstance(id);
io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat);
@ -372,8 +372,8 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
}
}
protected void handleShutdownVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
protected void handleShutdownVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp,
final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException {
try {
VmAction vm = serverAdapter.shutdownInstance(id);
io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat);
@ -382,8 +382,8 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
}
}
protected void handleGetDiskAttachmentsByVmId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
protected void handleGetDiskAttachmentsByVmId(final String id, final HttpServletResponse resp,
final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException {
try {
List<DiskAttachment> disks = serverAdapter.listDiskAttachmentsByInstanceUuid(id);
NamedList<DiskAttachment> response = NamedList.of("disk_attachment", disks);
@ -399,15 +399,15 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
String data = RouteHandler.getRequestData(req, logger);
try {
DiskAttachment request = io.getMapper().jsonMapper().readValue(data, DiskAttachment.class);
DiskAttachment response = serverAdapter.handleInstanceAttachDisk(id, request);
DiskAttachment response = serverAdapter.attachInstanceDisk(id, request);
io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat);
} catch (JsonProcessingException | CloudRuntimeException e) {
io.badRequest(resp, e.getMessage(), outFormat);
}
}
protected void handleGetNicsByVmId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
protected void handleGetNicsByVmId(final String id, final HttpServletResponse resp,
final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException {
try {
List<Nic> nics = serverAdapter.listNicsByInstanceUuid(id);
NamedList<Nic> response = NamedList.of("nic", nics);
@ -423,7 +423,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
String data = RouteHandler.getRequestData(req, logger);
try {
Nic request = io.getMapper().jsonMapper().readValue(data, Nic.class);
Nic response = serverAdapter.handleAttachInstanceNic(id, request);
Nic response = serverAdapter.attachInstanceNic(id, request);
io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat);
} catch (JsonProcessingException | CloudRuntimeException e) {
io.badRequest(resp, e.getMessage(), outFormat);
@ -447,7 +447,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
String data = RouteHandler.getRequestData(req, logger);
try {
Snapshot request = io.getMapper().jsonMapper().readValue(data, Snapshot.class);
Snapshot response = serverAdapter.handleCreateInstanceSnapshot(id, request);
Snapshot response = serverAdapter.createInstanceSnapshot(id, request);
io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, response, outFormat);
} catch (JsonProcessingException | CloudRuntimeException e) {
io.badRequest(resp, e.getMessage(), outFormat);
@ -455,7 +455,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
}
protected void handleGetSnapshotById(final String id, final HttpServletResponse resp,
final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException {
final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException {
try {
Snapshot response = serverAdapter.getSnapshot(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
@ -484,9 +484,13 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
protected void handleRestoreSnapshotById(final String id, final HttpServletRequest req,
final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io)
throws IOException {
//ToDo: implement
String data = RouteHandler.getRequestData(req, logger);
io.badRequest(resp, "Not implemented", outFormat);
try {
ResourceAction response = serverAdapter.revertInstanceToSnapshot(id);
io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, response, outFormat);
} catch (CloudRuntimeException e) {
io.badRequest(resp, e.getMessage(), outFormat);
}
}
protected void handleGetBackupsByVmId(final String id, final HttpServletResponse resp,

View File

@ -18,6 +18,8 @@
package org.apache.cloudstack.veeam.api.converter;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.cloudstack.jobs.JobInfo;
import org.apache.cloudstack.veeam.VeeamControlService;
@ -83,6 +85,10 @@ public class AsyncJobJoinVOToJobConverter {
return job;
}
public static List<Job> toJobList(List<AsyncJobJoinVO> vos) {
return vos.stream().map(AsyncJobJoinVOToJobConverter::toJob).collect(Collectors.toList());
}
protected static void fillAction(final ResourceAction action, final AsyncJobJoinVO vo) {
final String basePath = VeeamControlService.ContextPath.value();
action.setJob(Ref.of(basePath + JobsRouteHandler.BASE_ROUTE + vo.getUuid(), vo.getUuid()));

View File

@ -160,16 +160,16 @@ public final class UserVmJoinVOToVmConverter {
basePath + ApiService.BASE_ROUTE + "/cpuprofiles/" + src.getServiceOfferingUuid(),
src.getServiceOfferingUuid()));
if (allContent) {
dst.setInitialization(getOvfInitialization(dst));
dst.setInitialization(getOvfInitialization(dst, src));
}
return dst;
}
private static Vm.Initialization getOvfInitialization(Vm vm) {
private static Vm.Initialization getOvfInitialization(Vm vm, UserVmJoinVO vo) {
final Vm.Initialization.Configuration configuration = new Vm.Initialization.Configuration();
configuration.setType("ovf");
configuration.setData(OvfXmlUtil.toXml(vm));
configuration.setData(OvfXmlUtil.toXml(vm, vo));
final Vm.Initialization initialization = new Vm.Initialization();
initialization.setConfiguration(configuration);

View File

@ -19,6 +19,7 @@ package org.apache.cloudstack.veeam.api.converter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.cloudstack.backup.Backup;
@ -34,14 +35,12 @@ import org.apache.cloudstack.veeam.api.dto.Ref;
import org.apache.cloudstack.veeam.api.dto.StorageDomain;
import org.apache.cloudstack.veeam.api.dto.Vm;
import com.cloud.api.ApiDBUtils;
import com.cloud.api.query.vo.VolumeJoinVO;
import com.cloud.storage.Storage;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeStats;
public class VolumeJoinVOToDiskConverter {
public static Disk toDisk(final VolumeJoinVO vol) {
public static Disk toDisk(final VolumeJoinVO vol, final Function<VolumeJoinVO, Long> physicalSizeResolver) {
final Disk disk = new Disk();
final String basePath = VeeamControlService.ContextPath.value();
final String apiBasePath = basePath + ApiService.BASE_ROUTE;
@ -64,19 +63,12 @@ public class VolumeJoinVOToDiskConverter {
disk.setProvisionedSize(String.valueOf(size));
disk.setActualSize(String.valueOf(actualSize));
disk.setTotalSize(String.valueOf(size));
VolumeStats vs = null;
if (List.of(Storage.ImageFormat.VHD, Storage.ImageFormat.QCOW2, Storage.ImageFormat.RAW).contains(vol.getFormat())) {
if (vol.getPath() != null) {
vs = ApiDBUtils.getVolumeStatistics(vol.getPath());
}
} else if (vol.getFormat() == Storage.ImageFormat.OVA) {
if (vol.getChainInfo() != null) {
vs = ApiDBUtils.getVolumeStatistics(vol.getChainInfo());
}
Long physicalSize = null;
if (physicalSizeResolver != null) {
physicalSize = physicalSizeResolver.apply(vol);
}
if (vs != null) {
disk.setTotalSize(String.valueOf(vs.getVirtualSize()));
disk.setActualSize(String.valueOf(vs.getPhysicalSize()));
if (physicalSize != null) {
disk.setActualSize(String.valueOf(physicalSize));
}
// Disk format
@ -122,9 +114,10 @@ public class VolumeJoinVOToDiskConverter {
return disk;
}
public static List<Disk> toDiskList(final List<VolumeJoinVO> srcList) {
public static List<Disk> toDiskList(final List<VolumeJoinVO> srcList,
final Function<VolumeJoinVO, Long> physicalSizeResolver) {
return srcList.stream()
.map(VolumeJoinVOToDiskConverter::toDisk)
.map(vo -> toDisk(vo, physicalSizeResolver))
.collect(Collectors.toList());
}
@ -143,7 +136,8 @@ public class VolumeJoinVOToDiskConverter {
return disks;
}
public static DiskAttachment toDiskAttachment(final VolumeJoinVO vol) {
public static DiskAttachment toDiskAttachment(final VolumeJoinVO vol,
final Function<VolumeJoinVO, Long> physicalSizeResolver) {
final DiskAttachment da = new DiskAttachment();
final String basePath = VeeamControlService.ContextPath.value();
@ -154,7 +148,7 @@ public class VolumeJoinVOToDiskConverter {
da.setHref(da.getVm().getHref() + "/diskattachments/" + diskAttachmentId);;
// Links
da.setDisk(toDisk(vol));
da.setDisk(toDisk(vol, physicalSizeResolver));
// Properties
da.setActive("true");
@ -167,9 +161,10 @@ public class VolumeJoinVOToDiskConverter {
return da;
}
public static List<DiskAttachment> toDiskAttachmentList(final List<VolumeJoinVO> srcList) {
public static List<DiskAttachment> toDiskAttachmentList(final List<VolumeJoinVO> srcList,
final Function<VolumeJoinVO, Long> physicalSizeResolver) {
return srcList.stream()
.map(VolumeJoinVOToDiskConverter::toDiskAttachment)
.map(vo -> toDiskAttachment(vo, physicalSizeResolver))
.collect(Collectors.toList());
}
@ -190,9 +185,9 @@ public class VolumeJoinVOToDiskConverter {
if (state == null) {
return "ok";
}
switch (state.name().toLowerCase()) {
case "ready":
case "allocated":
switch (state) {
case Ready:
case Allocated:
return "ok";
default:
return "locked";

View File

@ -42,6 +42,8 @@ import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.cloud.api.query.vo.UserVmJoinVO;
public class OvfXmlUtil {
private static final String NS_OVF = "http://schemas.dmtf.org/ovf/envelope/1/";
@ -58,7 +60,7 @@ public class OvfXmlUtil {
return sdf;
});
public static String toXml(final Vm vm) {
public static String toXml(final Vm vm, final UserVmJoinVO vo) {
final String vmId = vm.getId();
final String vmName = vm.getName();
final String vmDesc = defaultString(vm.getDescription());
@ -169,6 +171,32 @@ public class OvfXmlUtil {
}
sb.append("</Section>");
if (vo != null) {
// -- Add a section for CloudStack-specific metadata that some consumers might look for (e.g. for import back into CloudStack) ---
// Add CloudStack-specific metadata section
sb.append("<Section xsi:type=\"ovf:CloudStackMetadata_Type\">");
sb.append("<Info>CloudStack specific metadata</Info>");
sb.append("<CloudStack>");
sb.append("<AccountId>").append(vo.getAccountUuid()).append("</AccountId>");
sb.append("<DomainId>").append(vo.getDomainUuid()).append("</DomainId>");
sb.append("<ProjectId>").append(escapeText(vo.getProjectUuid())).append("</ProjectId>");
sb.append("<ServiceOfferingId>").append(vo.getServiceOfferingUuid()).append("</ServiceOfferingId>");
sb.append("<DataDiskOfferingIdMap>");
for (DiskAttachment da : diskAttachments(vm)) {
if (da == null || da.getDisk() == null || StringUtils.isBlank(da.getDisk().getId())) {
continue;
}
final org.apache.cloudstack.veeam.api.dto.Disk d = da.getDisk();
sb.append("<Entry>");
sb.append("<DiskId>").append(escapeText(d.getId())).append("</DiskId>");
sb.append("<OfferingId>").append(d.getDiskProfile().getId()).append("</OfferingId>");
sb.append("</Entry>");
}
sb.append("</DataDiskOfferingIdMap>");
sb.append("</CloudStack>");
sb.append("</Section>");
}
// --- Content / VirtualSystem ---
sb.append("<Content ovf:id=\"out\" xsi:type=\"ovf:VirtualSystem_Type\">");
sb.append("<Name>").append(escapeText(vmName)).append("</Name>");
@ -191,7 +219,7 @@ public class OvfXmlUtil {
sb.append("<IsRunAndPause>false</IsRunAndPause>");
sb.append("<AutoStartup>false</AutoStartup>");
sb.append("<Priority>0</Priority>");
sb.append("<CreatedByUserId>").append(ZERO_UUID).append("</CreatedByUserId>");
sb.append("<CreatedByUserId>").append(vo.getAccountUuid()).append("</CreatedByUserId>");
sb.append("<MigrationSupport>0</MigrationSupport>");
sb.append("<IsBootMenuEnabled>").append(escapeText(booleanString(vm.getBios() != null && vm.getBios().getBootMenu() != null ? vm.getBios().getBootMenu().getEnabled() : null, "false"))).append("</IsBootMenuEnabled>");
sb.append("<IsSpiceFileTransferEnabled>true</IsSpiceFileTransferEnabled>");

View File

@ -17,18 +17,22 @@
package org.apache.cloudstack.network.contrail.management;
import java.net.InetAddress;
import java.util.List;
import java.util.Map;
import java.net.InetAddress;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.RolePermissionEntity;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
import org.apache.cloudstack.acl.apikeypair.ApiKeyPairPermission;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
import org.apache.cloudstack.api.command.admin.user.DeleteUserKeysCmd;
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
@ -37,20 +41,15 @@ import org.apache.cloudstack.api.command.admin.user.ListUserKeysCmd;
import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
import org.apache.cloudstack.api.command.admin.user.RegisterUserKeysCmd;
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import org.apache.cloudstack.api.response.ApiKeyPairResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
import org.apache.cloudstack.backup.BackupOffering;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.response.ApiKeyPairResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
import org.apache.cloudstack.context.CallContext;
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
import com.cloud.api.query.vo.ControlledViewEntity;
import com.cloud.configuration.ResourceLimit;
import com.cloud.configuration.dao.ResourceCountDao;
@ -614,4 +613,14 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
@Override
public void checkCallerRoleTypeAllowedForUserOrAccountOperations(Account userAccount, User user) {
}
@Override
public Account getActiveAccountByUuid(String accountUuid) {
return null;
}
@Override
public User getOneActiveUserForAccount(Account account) {
return null;
}
}

View File

@ -16,6 +16,8 @@
// under the License.
package com.cloud.api.query.dao;
import java.util.List;
import org.apache.cloudstack.api.response.AsyncJobResponse;
import org.apache.cloudstack.framework.jobs.AsyncJob;
@ -28,4 +30,6 @@ public interface AsyncJobJoinDao extends GenericDao<AsyncJobJoinVO, Long> {
AsyncJobJoinVO newAsyncJobView(AsyncJob vol);
List<AsyncJobJoinVO> listByIds(List<Long> ids);
}

View File

@ -16,17 +16,17 @@
// under the License.
package com.cloud.api.query.dao;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.inject.Inject;
import org.springframework.stereotype.Component;
import org.apache.cloudstack.api.ResponseObject;
import org.apache.cloudstack.api.response.AsyncJobResponse;
import org.apache.cloudstack.framework.jobs.AsyncJob;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;
import com.cloud.api.ApiResponseHelper;
import com.cloud.api.ApiSerializerHelper;
@ -115,4 +115,16 @@ public class AsyncJobJoinDaoImpl extends GenericDaoBase<AsyncJobJoinVO, Long> im
}
@Override
public List<AsyncJobJoinVO> listByIds(List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
return Collections.emptyList();
}
SearchBuilder<AsyncJobJoinVO> idsSearch = createSearchBuilder();
idsSearch.and("ids", idsSearch.entity().getId(), SearchCriteria.Op.IN);
idsSearch.done();
SearchCriteria<AsyncJobJoinVO> sc = idsSearch.create();
sc.setParameters("ids", ids.toArray());
return listBy(sc);
}
}

View File

@ -2755,6 +2755,11 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
return _accountDao.findById(accountId);
}
@Override
public Account getActiveAccountByUuid(String accountUuid) {
return _accountDao.findByUuid(accountUuid);
}
@Override
public Account getAccount(long accountId) {
return _accountDao.findByIdIncludingRemoved(accountId);
@ -2773,6 +2778,15 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
return _userDao.findById(userId);
}
@Override
public User getOneActiveUserForAccount(Account account) {
List<UserVO> users = _userDao.listByAccount(account.getId());
if (CollectionUtils.isEmpty(users)) {
return null;
}
return users.get(0);
}
@Override
public User getUserIncludingRemoved(long userId) {
return _userDao.findByIdIncludingRemoved(userId);

View File

@ -8017,7 +8017,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
logger.trace("Verifying if the new account [{}] has access to the specified domain [{}].", newAccount, domain);
_accountMgr.checkAccess(newAccount, domain);
Network newNetwork = ensureDestinationNetwork(cmd, vm, newAccount);
Network newNetwork = null;
if (!cmd.isSkipNetwork()) {
newNetwork = ensureDestinationNetwork(cmd, vm, newAccount);
}
try {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override

View File

@ -158,7 +158,7 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
backup.setAccountId(vm.getAccountId());
backup.setDomainId(vm.getDomainId());
backup.setZoneId(vm.getDataCenterId());
backup.setStatus(Backup.Status.ReadyForTransfer);
backup.setStatus(Backup.Status.Queued);
backup.setBackupOfferingId(vm.getBackupOfferingId());
backup.setDate(new Date());
@ -236,6 +236,7 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
// todo: set it in the backend
backup.setType("Incremental");
}
updateBackupState(backup, Backup.Status.ReadyForTransfer);
return backup;
}
@ -308,12 +309,12 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
// Delete old checkpoint if exists (POC: skip actual libvirt call)
if (oldCheckpointId != null) {
// todo: In production: send command to delete oldCheckpointId via virsh checkpoint-delete
logger.debug("Would delete old checkpoint: " + oldCheckpointId);
logger.debug("Would delete old checkpoint: {}", oldCheckpointId);
}
// Delete backup session record
backup.setStatus(Backup.Status.BackedUp);
backupDao.update(backupId, backup);
updateBackupState(backup, Backup.Status.BackedUp);
backupDao.remove(backup.getId());
return backup;