worker vm deployment wip

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
Abhishek Kumar 2026-02-06 14:05:13 +05:30
parent ca4112e7d0
commit a3669298af
42 changed files with 2435 additions and 685 deletions

View File

@ -48,4 +48,12 @@ public class DeployVMCmdByAdmin extends DeployVMCmd implements AdminCmd {
public Long getClusterId() {
return clusterId;
}
/////////////////////////////////////////////////////
////////////////// Setters //////////////////////////
/////////////////////////////////////////////////////
public void setClusterId(Long clusterId) {
this.clusterId = clusterId;
}
}

View File

@ -193,6 +193,18 @@ public class ListServiceOfferingsCmd extends BaseListProjectAndAccountResourcesC
return gpuEnabled;
}
public void setZoneId(Long zoneId) {
this.zoneId = zoneId;
}
public void setCpuNumber(Integer cpuNumber) {
this.cpuNumber = cpuNumber;
}
public void setMemory(Integer memory) {
this.memory = memory;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -100,6 +100,26 @@ public class AddNicToVMCmd extends BaseAsyncCmd implements UserCmd {
return NetUtils.standardizeMacAddress(macaddr);
}
public void setVmId(Long vmId) {
this.vmId = vmId;
}
public void setNetworkId(Long netId) {
this.netId = netId;
}
public void setIpaddr(String ipaddr) {
this.ipaddr = ipaddr;
}
public void setMacAddress(String macaddr) {
this.macaddr = macaddr;
}
public void setDhcpOptions(Map dhcpOptions) {
this.dhcpOptions = dhcpOptions;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -798,6 +798,218 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme
}
return null;
}
/////////////////////////////////////////////////////
////////////////// Setters //////////////////////////
/////////////////////////////////////////////////////
public void setZoneId(Long zoneId) {
this.zoneId = zoneId;
}
public void setName(String name) {
this.name = name;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public void setPassword(String password) {
this.password = password;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public void setDomainId(Long domainId) {
this.domainId = domainId;
}
public void setNetworkIds(List<Long> networkIds) {
this.networkIds = networkIds;
}
public void setBootType(String bootType) {
this.bootType = bootType;
}
public void setBootMode(String bootMode) {
this.bootMode = bootMode;
}
public void setBootIntoSetup(Boolean bootIntoSetup) {
this.bootIntoSetup = bootIntoSetup;
}
public void setDiskOfferingId(Long diskOfferingId) {
this.diskOfferingId = diskOfferingId;
}
public void setSize(Long size) {
this.size = size;
}
public void setRootdisksize(Long rootdisksize) {
this.rootdisksize = rootdisksize;
}
public void setDataDisksDetails(Map dataDisksDetails) {
this.dataDisksDetails = dataDisksDetails;
}
public void setGroup(String group) {
this.group = group;
}
public void setHypervisor(String hypervisor) {
this.hypervisor = hypervisor;
}
public void setUserData(String userData) {
this.userData = userData;
}
public void setUserdataId(Long userdataId) {
this.userdataId = userdataId;
}
public void setUserdataDetails(Map userdataDetails) {
this.userdataDetails = userdataDetails;
}
public void setSshKeyPairName(String sshKeyPairName) {
this.sshKeyPairName = sshKeyPairName;
}
public void setSshKeyPairNames(List<String> sshKeyPairNames) {
this.sshKeyPairNames = sshKeyPairNames;
}
public void setHostId(Long hostId) {
this.hostId = hostId;
}
public void setSecurityGroupIdList(List<Long> securityGroupIdList) {
this.securityGroupIdList = securityGroupIdList;
}
public void setSecurityGroupNameList(List<String> securityGroupNameList) {
this.securityGroupNameList = securityGroupNameList;
}
public void setIpToNetworkList(Map ipToNetworkList) {
this.ipToNetworkList = ipToNetworkList;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
public void setIp6Address(String ip6Address) {
this.ip6Address = ip6Address;
}
public void setMacAddress(String macAddress) {
this.macAddress = macAddress;
}
public void setKeyboard(String keyboard) {
this.keyboard = keyboard;
}
public void setProjectId(Long projectId) {
this.projectId = projectId;
}
public void setStartVm(Boolean startVm) {
this.startVm = startVm;
}
public void setAffinityGroupIdList(List<Long> affinityGroupIdList) {
this.affinityGroupIdList = affinityGroupIdList;
}
public void setAffinityGroupNameList(List<String> affinityGroupNameList) {
this.affinityGroupNameList = affinityGroupNameList;
}
public void setDisplayVm(Boolean displayVm) {
this.displayVm = displayVm;
}
public void setDetails(Map details) {
this.details = details;
}
public void setDeploymentPlanner(String deploymentPlanner) {
this.deploymentPlanner = deploymentPlanner;
}
public void setDhcpOptionsNetworkList(Map dhcpOptionsNetworkList) {
this.dhcpOptionsNetworkList = dhcpOptionsNetworkList;
}
public void setDataDiskTemplateToDiskOfferingList(Map dataDiskTemplateToDiskOfferingList) {
this.dataDiskTemplateToDiskOfferingList = dataDiskTemplateToDiskOfferingList;
}
public void setExtraConfig(String extraConfig) {
this.extraConfig = extraConfig;
}
public void setCopyImageTags(Boolean copyImageTags) {
this.copyImageTags = copyImageTags;
}
public void setvAppProperties(Map vAppProperties) {
this.vAppProperties = vAppProperties;
}
public void setvAppNetworks(Map vAppNetworks) {
this.vAppNetworks = vAppNetworks;
}
public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) {
this.dynamicScalingEnabled = dynamicScalingEnabled;
}
public void setOverrideDiskOfferingId(Long overrideDiskOfferingId) {
this.overrideDiskOfferingId = overrideDiskOfferingId;
}
public void setIothreadsEnabled(Boolean iothreadsEnabled) {
this.iothreadsEnabled = iothreadsEnabled;
}
public void setIoDriverPolicy(String ioDriverPolicy) {
this.ioDriverPolicy = ioDriverPolicy;
}
public void setNicMultiqueueNumber(Integer nicMultiqueueNumber) {
this.nicMultiqueueNumber = nicMultiqueueNumber;
}
public void setNicPackedVirtQueues(Boolean nicPackedVirtQueues) {
this.nicPackedVirtQueues = nicPackedVirtQueues;
}
public void setLeaseDuration(Integer leaseDuration) {
this.leaseDuration = leaseDuration;
}
public void setLeaseExpiryAction(String leaseExpiryAction) {
this.leaseExpiryAction = leaseExpiryAction;
}
public void setExternalDetails(Map externalDetails) {
this.externalDetails = externalDetails;
}
public void setDataDiskInfoList(List<VmDiskInfo> dataDiskInfoList) {
this.dataDiskInfoList = dataDiskInfoList;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -64,8 +64,8 @@ public class DeployVMCmd extends BaseDeployVMCmd {
@Parameter(name = ApiConstants.SNAPSHOT_ID, type = CommandType.UUID, entityType = SnapshotResponse.class, since = "4.21")
private Long snapshotId;
@Parameter(name = ApiConstants.DUMMY, type = CommandType.BOOLEAN, since = "4.23", description = "Deploy a dummy VM without any disk. False by default. This supports KVM only.")
private Boolean dummy;
@Parameter(name = "blank", type = CommandType.BOOLEAN, since = "4.22.1")
private Boolean blankInstance;
/////////////////////////////////////////////////////
@ -88,14 +88,38 @@ public class DeployVMCmd extends BaseDeployVMCmd {
return snapshotId;
}
public boolean getDummy() {
return dummy != null && dummy;
}
public boolean isVolumeOrSnapshotProvided() {
return volumeId != null || snapshotId != null;
}
public boolean isBlankInstance() {
return Boolean.TRUE.equals(blankInstance);
}
/////////////////////////////////////////////////////
////////////////// Setters //////////////////////////
/////////////////////////////////////////////////////
public void setServiceOfferingId(Long serviceOfferingId) {
this.serviceOfferingId = serviceOfferingId;
}
public void setTemplateId(Long templateId) {
this.templateId = templateId;
}
public void setVolumeId(Long volumeId) {
this.volumeId = volumeId;
}
public void setSnapshotId(Long snapshotId) {
this.snapshotId = snapshotId;
}
public void setBlankInstance(boolean blankInstance) {
this.blankInstance = blankInstance;
}
@Override
public void execute() {
UserVm result;
@ -140,7 +164,7 @@ public class DeployVMCmd extends BaseDeployVMCmd {
@Override
public void create() throws ResourceAllocationException {
if (!getDummy() && Stream.of(templateId, snapshotId, volumeId).filter(Objects::nonNull).count() != 1) {
if (!isBlankInstance() && Stream.of(templateId, snapshotId, volumeId).filter(Objects::nonNull).count() != 1) {
throw new CloudRuntimeException("Please provide only one of the following parameters - template ID, volume ID or snapshot ID");
}

View File

@ -64,12 +64,16 @@ public interface IncrementalBackupService extends Configurable, PluggableService
ImageTransfer createImageTransfer(long volumeId, Long backupId, ImageTransfer.Direction direction);
boolean cancelImageTransfer(long imageTransferId);
/**
* Finalize an image transfer
* Marks transfer as complete (NBD is closed globally in finalize backup)
*/
boolean finalizeImageTransfer(FinalizeImageTransferCmd cmd);
boolean finalizeImageTransfer(long imageTransferId);
/**
* List image transfers for a backup
*/

View File

@ -577,7 +577,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
logger.debug("Allocating disks for {}", persistedVm);
if (_userVmMgr.isDummyTemplate(hyperType, template.getId())) {
if (_userVmMgr.isBlankInstanceTemplate(template)) {
logger.debug("Template is a dummy template for hypervisor {}, skipping volume allocation", hyperType);
return;
} else {

View File

@ -0,0 +1,833 @@
// 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.veeam.adapter;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import javax.inject.Inject;
import org.apache.cloudstack.acl.Role;
import org.apache.cloudstack.acl.RolePermissionEntity;
import org.apache.cloudstack.acl.RoleService;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.Rule;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
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;
import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd;
import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd;
import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd;
import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
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.volume.AttachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.DeleteVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
import org.apache.cloudstack.backup.ImageTransfer.Direction;
import org.apache.cloudstack.backup.ImageTransferVO;
import org.apache.cloudstack.backup.IncrementalBackupService;
import org.apache.cloudstack.backup.dao.ImageTransferDao;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.query.QueryService;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.veeam.api.converter.AsyncJobJoinVOToJobConverter;
import org.apache.cloudstack.veeam.api.converter.ClusterVOToClusterConverter;
import org.apache.cloudstack.veeam.api.converter.DataCenterJoinVOToDataCenterConverter;
import org.apache.cloudstack.veeam.api.converter.HostJoinVOToHostConverter;
import org.apache.cloudstack.veeam.api.converter.ImageTransferVOToImageTransferConverter;
import org.apache.cloudstack.veeam.api.converter.NetworkVOToNetworkConverter;
import org.apache.cloudstack.veeam.api.converter.NetworkVOToVnicProfileConverter;
import org.apache.cloudstack.veeam.api.converter.NicVOToNicConverter;
import org.apache.cloudstack.veeam.api.converter.StoreVOToStorageDomainConverter;
import org.apache.cloudstack.veeam.api.converter.UserVmJoinVOToVmConverter;
import org.apache.cloudstack.veeam.api.converter.VolumeJoinVOToDiskConverter;
import org.apache.cloudstack.veeam.api.dto.Cluster;
import org.apache.cloudstack.veeam.api.dto.DataCenter;
import org.apache.cloudstack.veeam.api.dto.Disk;
import org.apache.cloudstack.veeam.api.dto.DiskAttachment;
import org.apache.cloudstack.veeam.api.dto.Host;
import org.apache.cloudstack.veeam.api.dto.ImageTransfer;
import org.apache.cloudstack.veeam.api.dto.Job;
import org.apache.cloudstack.veeam.api.dto.Network;
import org.apache.cloudstack.veeam.api.dto.Nic;
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 org.apache.cloudstack.veeam.api.dto.VmAction;
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 com.cloud.api.query.dao.DataCenterJoinDao;
import com.cloud.api.query.dao.HostJoinDao;
import com.cloud.api.query.dao.ImageStoreJoinDao;
import com.cloud.api.query.dao.StoragePoolJoinDao;
import com.cloud.api.query.dao.UserVmJoinDao;
import com.cloud.api.query.dao.VolumeJoinDao;
import com.cloud.api.query.vo.DataCenterJoinVO;
import com.cloud.api.query.vo.HostJoinVO;
import com.cloud.api.query.vo.ImageStoreJoinVO;
import com.cloud.api.query.vo.StoragePoolJoinVO;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.api.query.vo.VolumeJoinVO;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenterVO;
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.OperationTimedoutException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.offering.ServiceOffering;
import com.cloud.org.Grouping;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeApiService;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.dao.VolumeDetailsDao;
import com.cloud.user.Account;
import com.cloud.user.AccountService;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.UserAccount;
import com.cloud.user.dao.AccountDao;
import com.cloud.uservm.UserVm;
import com.cloud.utils.EnumUtils;
import com.cloud.utils.component.ComponentContext;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.NicVO;
import com.cloud.vm.UserVmService;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.UserVmDao;
public class ServerAdapter extends ManagerBase {
private static final String SERVICE_ACCOUNT_NAME = "veemserviceuser";
private static final String SERVICE_ACCOUNT_ROLE_NAME = "Veeam Service Role";
private static final String SERVICE_ACCOUNT_FIRST_NAME = "Veeam";
private static final String SERVICE_ACCOUNT_LAST_NAME = "Service User";
private static final List<Class<?>> SERVICE_ACCOUNT_ROLE_ALLOWED_APIS = Arrays.asList(
QueryAsyncJobResultCmd.class,
ListVMsCmd.class,
DeployVMCmd.class,
StartVMCmd.class,
StopVMCmd.class,
DestroyVMCmd.class,
ListVolumesCmd.class,
CreateVolumeCmd.class,
DeleteVolumeCmd.class,
AttachVolumeCmd.class,
DetachVolumeCmd.class,
ResizeVolumeCmd.class,
ListNetworksCmd.class
);
@Inject
RoleService roleService;
@Inject
AccountService accountService;
@Inject
AccountDao accountDao;
@Inject
DataCenterDao dataCenterDao;
@Inject
DataCenterJoinDao dataCenterJoinDao;
@Inject
StoragePoolJoinDao storagePoolJoinDao;
@Inject
ImageStoreJoinDao imageStoreJoinDao;
@Inject
ClusterDao clusterDao;
@Inject
HostJoinDao hostJoinDao;
@Inject
NetworkDao networkDao;
@Inject
UserVmDao userVmDao;
@Inject
UserVmJoinDao userVmJoinDao;
@Inject
VolumeDao volumeDao;
@Inject
VolumeJoinDao volumeJoinDao;
@Inject
VolumeDetailsDao volumeDetailsDao;
@Inject
VolumeApiService volumeApiService;
@Inject
PrimaryDataStoreDao primaryDataStoreDao;
@Inject
ImageTransferDao imageTransferDao;
@Inject
IncrementalBackupService incrementalBackupService;
@Inject
QueryService queryService;
@Inject
ServiceOfferingDao serviceOfferingDao;
@Inject
UserVmService userVmService;
@Inject
NicDao nicDao;
private Map<String, Long> jobsMap = new ConcurrentHashMap<>();
protected Role createServiceAccountRole() {
Role role = roleService.createRole(SERVICE_ACCOUNT_ROLE_NAME, RoleType.User,
SERVICE_ACCOUNT_ROLE_NAME, false);
for (Class<?> allowedApi : SERVICE_ACCOUNT_ROLE_ALLOWED_APIS) {
final String apiName = BaseCmd.getCommandNameByClass(allowedApi);
roleService.createRolePermission(role, new Rule(apiName), RolePermissionEntity.Permission.ALLOW,
String.format("Allow %s", apiName));
}
roleService.createRolePermission(role, new Rule("*"), RolePermissionEntity.Permission.DENY,
"Deny all");
logger.debug("Created default role for Veeam service account in projects: {}", role);
return role;
}
public Role getServiceAccountRole() {
List<Role> roles = roleService.findRolesByName(SERVICE_ACCOUNT_ROLE_NAME);
if (CollectionUtils.isNotEmpty(roles)) {
Role role = roles.get(0);
logger.debug("Found default role for Veeam service account in projects: {}", role);
return role;
}
return createServiceAccountRole();
}
protected Account createServiceAccount() {
CallContext.register(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM);
try {
Role role = getServiceAccountRole();
UserAccount userAccount = accountService.createUserAccount(SERVICE_ACCOUNT_NAME,
UUID.randomUUID().toString(), SERVICE_ACCOUNT_FIRST_NAME,
SERVICE_ACCOUNT_LAST_NAME, null, null, SERVICE_ACCOUNT_NAME, Account.Type.NORMAL, role.getId(),
1L, null, null, null, null, User.Source.NATIVE);
Account account = accountService.getAccount(userAccount.getAccountId());
logger.debug("Created Veeam service account: {}", account);
return account;
} finally {
CallContext.unregister();
}
}
protected Account createServiceAccountIfNeeded() {
List<AccountVO> accounts = accountDao.findAccountsByName(SERVICE_ACCOUNT_NAME);
for (AccountVO account : accounts) {
if (Account.State.ENABLED.equals(account.getState())) {
logger.debug("Veeam service account found: {}", account);
return account;
}
}
return createServiceAccount();
}
@Override
public boolean start() {
createServiceAccountIfNeeded();
//find public custom disk offering
return true;
}
public List<DataCenter> listAllDataCenters() {
final List<DataCenterJoinVO> clusters = dataCenterJoinDao.listAll();
return DataCenterJoinVOToDataCenterConverter.toDCList(clusters);
}
public DataCenter getDataCenter(String uuid) {
final DataCenterJoinVO vo = dataCenterJoinDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("DataCenter with ID " + uuid + " not found");
}
return DataCenterJoinVOToDataCenterConverter.toDataCenter(vo);
}
public List<StorageDomain> listStorageDomainsByDcId(final String uuid) {
final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(uuid);
if (dataCenterVO == null) {
throw new InvalidParameterValueException("DataCenter with ID " + uuid + " not found");
}
List<StoragePoolJoinVO> storagePoolVOS = storagePoolJoinDao.listAll();
List<StorageDomain> storageDomains = StoreVOToStorageDomainConverter.toStorageDomainListFromPools(storagePoolVOS);
List<ImageStoreJoinVO> imageStoreJoinVOS = imageStoreJoinDao.listAll();
storageDomains.addAll(StoreVOToStorageDomainConverter.toStorageDomainListFromStores(imageStoreJoinVOS));
return storageDomains;
}
public List<Network> listNetworksByDcId(final String uuid) {
final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(uuid);
if (dataCenterVO == null) {
throw new InvalidParameterValueException("DataCenter with ID " + uuid + " not found");
}
List<NetworkVO> networks = networkDao.listAll();
return NetworkVOToNetworkConverter.toNetworkList(networks, (dcId) -> dataCenterVO);
}
public List<Cluster> listAllClusters() {
final List<ClusterVO> clusters = clusterDao.listAll();
return ClusterVOToClusterConverter.toClusterList(clusters, this::getZoneById);
}
public Cluster getCluster(String uuid) {
final ClusterVO vo = clusterDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Cluster with ID " + uuid + " not found");
}
return ClusterVOToClusterConverter.toCluster(vo, this::getZoneById);
}
public List<Host> listAllHosts() {
final List<HostJoinVO> hosts = hostJoinDao.listAll();
return HostJoinVOToHostConverter.toHostList(hosts);
}
public Host getHost(String uuid) {
final HostJoinVO vo = hostJoinDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Host with ID " + uuid + " not found");
}
return HostJoinVOToHostConverter.toHost(vo);
}
public List<Network> listAllNetworks() {
final List<NetworkVO> networks = networkDao.listAll();
return NetworkVOToNetworkConverter.toNetworkList(networks, this::getZoneById);
}
public Network getNetwork(String uuid) {
final NetworkVO vo = networkDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Host with ID " + uuid + " not found");
}
return NetworkVOToNetworkConverter.toNetwork(vo, this::getZoneById);
}
public List<VnicProfile> listAllVnicProfiles() {
final List<NetworkVO> networks = networkDao.listAll();
return NetworkVOToVnicProfileConverter.toVnicProfileList(networks, this::getZoneById);
}
public VnicProfile getVnicProfile(String uuid) {
final NetworkVO vo = networkDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Host with ID " + uuid + " not found");
}
return NetworkVOToVnicProfileConverter.toVnicProfile(vo, this::getZoneById);
}
public List<Vm> listAllUserVms() {
// Todo: add filtering, pagination
List<UserVmJoinVO> vms = userVmJoinDao.listAll();
return UserVmJoinVOToVmConverter.toVmList(vms, this::getHostById);
}
public Vm getVm(String uuid) {
UserVmJoinVO vo = userVmJoinDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
}
return UserVmJoinVOToVmConverter.toVm(vo, this::getHostById, this::listDiskAttachmentsByInstanceId,
this::listNicsByInstance);
}
public Vm handleCreateVm(Vm request) {
if (request == null) {
throw new InvalidParameterValueException("Request disk data is empty");
}
String name = request.name;
Long zoneId = null;
Long clusterId = null;
if (request.cluster != null && StringUtils.isNotEmpty(request.cluster.id)) {
ClusterVO clusterVO = clusterDao.findByUuid(request.cluster.id);
if (clusterVO != null) {
zoneId = clusterVO.getDataCenterId();
clusterId = clusterVO.getId();
}
}
if (zoneId == null) {
throw new InvalidParameterValueException("Failed to determine datacenter for VM creation request");
}
Integer cpu = null;
try {
cpu = request.cpu.topology.sockets;
} catch (Exception ignored) {}
if (cpu == null) {
throw new InvalidParameterValueException("CPU topology sockets must be specified");
}
Long memory = null;
try {
memory = request.memory;
} catch (Exception ignored) {}
if (memory == null) {
throw new InvalidParameterValueException("Memory must be specified");
}
String userdata = null;
if (request.getInitialization() != null) {
userdata = request.getInitialization().getContentData();
}
ApiConstants.BootType bootType = ApiConstants.BootType.BIOS;
ApiConstants.BootMode bootMode = ApiConstants.BootMode.LEGACY;
if (request.bios != null && StringUtils.isNotEmpty(request.bios.type) && request.bios.type.contains("secure")) {
bootType = ApiConstants.BootType.UEFI;
bootMode = ApiConstants.BootMode.SECURE;
}
Account serviceAccount = createServiceAccountIfNeeded();
CallContext.register(serviceAccount.getId(), serviceAccount.getId());
try {
return createVm(zoneId, clusterId, name, cpu, memory, userdata, bootType, bootMode);
} finally {
CallContext.unregister();
}
}
protected ServiceOffering getServiceOfferingIdForVmCreation(long zoneId, int cpu, long memory) {
ListServiceOfferingsCmd cmd = new ListServiceOfferingsCmd();
ComponentContext.inject(cmd);
cmd.setZoneId(zoneId);
cmd.setCpuNumber(cpu);
Integer memoryMB = (int)(memory / (1024L * 1024L));
cmd.setMemory(memoryMB);
ListResponse<ServiceOfferingResponse> offerings = queryService.searchForServiceOfferings(cmd);
if (offerings.getResponses().isEmpty()) {
return null;
}
String uuid = offerings.getResponses().get(0).getId();
return serviceOfferingDao.findByUuid(uuid);
}
protected Vm createVm(Long zoneId, Long clusterId, String name, int cpu, long memory, String userdata,
ApiConstants.BootType bootType, ApiConstants.BootMode bootMode) {
ServiceOffering serviceOffering = getServiceOfferingIdForVmCreation(zoneId, cpu, memory);
if (serviceOffering == null) {
throw new CloudRuntimeException("No service offering found for VM creation with specified CPU and memory");
}
DeployVMCmdByAdmin cmd = new DeployVMCmdByAdmin();
ComponentContext.inject(cmd);
cmd.setZoneId(zoneId);
cmd.setClusterId(clusterId);
cmd.setName(name);
cmd.setServiceOfferingId(serviceOffering.getId());
if (StringUtils.isNotEmpty(userdata)) {
cmd.setUserData(Base64.getEncoder().encodeToString(userdata.getBytes()));
}
if (bootType != null) {
cmd.setBootType(bootType.toString());
}
if (bootMode != null) {
cmd.setBootMode(bootMode.toString());
}
// ToDo: handle other.
cmd.setHypervisor(Hypervisor.HypervisorType.KVM.name());
cmd.setBlankInstance(true);
try {
UserVm vm = userVmService.createVirtualMachine(cmd);
vm = userVmService.finalizeCreateVirtualMachine(vm.getId());
UserVmJoinVO vo = userVmJoinDao.findById(vm.getId());
return UserVmJoinVOToVmConverter.toVm(vo, this::getHostById, this::listDiskAttachmentsByInstanceId,
this::listNicsByInstance);
} catch (InsufficientCapacityException | ResourceUnavailableException | ResourceAllocationException | CloudRuntimeException e) {
throw new CloudRuntimeException("Failed to create VM: " + e.getMessage(), e);
}
}
public void deleteVm(String uuid) {
UserVmVO vo = userVmDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
}
try {
userVmService.destroyVm(vo.getId(), true);
} catch (ResourceUnavailableException e) {
throw new CloudRuntimeException("Failed to delete VM: " + e.getMessage(), e);
}
}
public VmAction startVm(String uuid) {
UserVmVO vo = userVmDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
}
try {
userVmService.startVirtualMachine(vo, null);
return UserVmJoinVOToVmConverter.toVmAction(userVmJoinDao.findById(vo.getId()));
} catch (ResourceUnavailableException | OperationTimedoutException | InsufficientCapacityException | CloudRuntimeException e) {
throw new CloudRuntimeException("Failed to start VM: " + e.getMessage(), e);
}
}
public VmAction stopVm(String uuid) {
UserVmVO vo = userVmDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
}
try {
userVmService.stopVirtualMachine(vo.getId(), true);
return UserVmJoinVOToVmConverter.toVmAction(userVmJoinDao.findById(vo.getId()));
} catch (CloudRuntimeException e) {
throw new CloudRuntimeException("Failed to stop VM: " + e.getMessage(), e);
}
}
public VmAction shutdownVm(String uuid) {
UserVmVO vo = userVmDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
}
try {
userVmService.stopVirtualMachine(vo.getId(), false);
return UserVmJoinVOToVmConverter.toVmAction(userVmJoinDao.findById(vo.getId()));
} catch (CloudRuntimeException e) {
throw new CloudRuntimeException("Failed to shutdown VM: " + e.getMessage(), e);
}
}
public List<Disk> listAllDisks() {
List<VolumeJoinVO> kvmVolumes = volumeJoinDao.listByHypervisor(Hypervisor.HypervisorType.KVM);
return VolumeJoinVOToDiskConverter.toDiskList(kvmVolumes);
}
public Disk getDisk(String uuid) {
VolumeJoinVO vo = volumeJoinDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Disk with ID " + uuid + " not found");
}
return VolumeJoinVOToDiskConverter.toDisk(vo);
}
protected List<DiskAttachment> listDiskAttachmentsByInstanceId(final long instanceId) {
List<VolumeJoinVO> kvmVolumes = volumeJoinDao.listByInstanceId(instanceId);
return VolumeJoinVOToDiskConverter.toDiskAttachmentList(kvmVolumes);
}
public List<DiskAttachment> listDiskAttachmentsByInstanceUuid(final String uuid) {
UserVmVO vo = userVmDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
}
return listDiskAttachmentsByInstanceId(vo.getId());
}
public DiskAttachment handleVmAttachDisk(final String vmUuid, final DiskAttachment request) {
UserVmVO vmVo = userVmDao.findByUuid(vmUuid);
if (vmVo == null) {
throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found");
}
if (request == null || request.disk == null || StringUtils.isEmpty(request.disk.id)) {
throw new InvalidParameterValueException("Request disk data is empty");
}
VolumeVO volumeVO = volumeDao.findByUuid(request.disk.id);
if (volumeVO == null) {
throw new InvalidParameterValueException("Disk with ID " + request.disk.id + " not found");
}
Account serviceAccount = createServiceAccountIfNeeded();
CallContext.register(serviceAccount.getId(), serviceAccount.getId());
try {
Volume volume = volumeApiService.attachVolumeToVM(vmVo.getId(), volumeVO.getId(), 0L, false);
VolumeJoinVO attachedVolumeVO = volumeJoinDao.findById(volume.getId());
return VolumeJoinVOToDiskConverter.toDiskAttachment(attachedVolumeVO);
} finally {
CallContext.unregister();
}
}
public void deleteDisk(String uuid) {
VolumeVO vo = volumeDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Disk with ID " + uuid + " not found");
}
volumeApiService.deleteVolume(vo.getId(), accountService.getSystemAccount());
}
public Disk handleCreateDisk(Disk request) {
if (request == null) {
throw new InvalidParameterValueException("Request disk data is empty");
}
String name = request.name;
if (StringUtils.isBlank(name) && !name.startsWith("Veeam_KvmBackupDisk_")) {
throw new InvalidParameterValueException("Only worker VM disk creation is supported");
}
if (request.storageDomains == null || CollectionUtils.isEmpty(request.storageDomains.storageDomain) ||
request.storageDomains.storageDomain.size() > 1) {
throw new InvalidParameterValueException("Exactly one storage domain must be specified");
}
Ref domain = request.storageDomains.storageDomain.get(0);
if (domain == null || domain.id == null) {
throw new InvalidParameterValueException("Storage domain ID must be specified");
}
StoragePoolVO pool = primaryDataStoreDao.findByUuid(domain.id);
if (pool == null) {
throw new InvalidParameterValueException("Storage domain with ID " + domain.id + " not found");
}
String sizeStr = request.provisionedSize;
if (StringUtils.isBlank(sizeStr)) {
throw new InvalidParameterValueException("Provisioned size must be specified");
}
long provisionedSizeInGb;
try {
provisionedSizeInGb = Long.parseLong(sizeStr);
} catch (NumberFormatException ex) {
throw new InvalidParameterValueException("Invalid provisioned size: " + sizeStr);
}
if (provisionedSizeInGb <= 0) {
throw new InvalidParameterValueException("Provisioned size must be greater than zero");
}
provisionedSizeInGb = Math.max(1L, provisionedSizeInGb / (1024L * 1024L * 1024L));
Long initialSize = null;
if (StringUtils.isNotBlank(request.initialSize)) {
try {
initialSize = Long.parseLong(request.initialSize);
} catch (NumberFormatException ignored) {}
}
Account serviceAccount = createServiceAccountIfNeeded();
DataCenterVO zone = dataCenterDao.findById(pool.getDataCenterId());
if (zone == null || !Grouping.AllocationState.Enabled.equals(zone.getAllocationState())) {
throw new InvalidParameterValueException("Datacenter for the specified storage domain is not found or not active");
}
Long diskOfferingId = volumeApiService.getCustomDiskOfferingIdForVolumeUpload(serviceAccount, zone);
if (diskOfferingId == null) {
throw new CloudRuntimeException("Failed to find custom offering for disk" + zone.getName());
}
CallContext.register(serviceAccount.getId(), serviceAccount.getId());
try {
return createDisk(serviceAccount, pool, name, diskOfferingId, provisionedSizeInGb, initialSize);
} finally {
CallContext.unregister();
}
}
@NotNull
private Disk createDisk(Account serviceAccount, StoragePoolVO pool, String name, Long diskOfferingId, long sizeInGb, Long initialSize) {
Volume volume;
try {
volume = volumeApiService.allocVolume(serviceAccount.getId(), pool.getDataCenterId(), diskOfferingId, null,
null, name, sizeInGb, null, null, null, null);
} catch (ResourceAllocationException e) {
throw new CloudRuntimeException(e.getMessage(), e);
}
if (volume == null) {
throw new CloudRuntimeException("Failed to create volume");
}
volume = volumeApiService.createVolume(volume.getId(), null, null, pool.getId(), true);
if (initialSize != null) {
volumeDetailsDao.addDetail(volume.getId(), ApiConstants.VIRTUAL_SIZE, String.valueOf(initialSize), true);
}
// Implementation for creating a Disk resource
return VolumeJoinVOToDiskConverter.toDisk(volumeJoinDao.findById(volume.getId()));
}
protected List<Nic> listNicsByInstance(final long instanceId, final String instanceUuid) {
List<NicVO> nics = nicDao.listByVmId(instanceId);
return NicVOToNicConverter.toNicList(nics, instanceUuid, this::getNetworkById);
}
protected List<Nic> listNicsByInstance(final UserVmJoinVO vo) {
return listNicsByInstance(vo.getId(), vo.getUuid());
}
public List<Nic> listNicsByInstanceId(final String uuid) {
UserVmVO vo = userVmDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("VM with ID " + uuid + " not found");
}
return listNicsByInstance(vo.getId(), vo.getUuid());
}
public Nic handleVmAttachNic(final String vmUuid, final Nic request) {
UserVmVO vmVo = userVmDao.findByUuid(vmUuid);
if (vmVo == null) {
throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found");
}
if (request == null || request.getVnicProfile() == null || StringUtils.isEmpty(request.getVnicProfile().id)) {
throw new InvalidParameterValueException("Request nic data is empty");
}
NetworkVO networkVO = networkDao.findByUuid(request.getVnicProfile().id);
if (networkVO == null) {
throw new InvalidParameterValueException("VNic profile " + request.getVnicProfile().id+ " not found");
}
Account serviceAccount = createServiceAccountIfNeeded();
CallContext.register(serviceAccount.getId(), serviceAccount.getId());
try {
AddNicToVMCmd cmd = new AddNicToVMCmd();
ComponentContext.inject(cmd);
cmd.setVmId(vmVo.getId());
cmd.setNetworkId(networkVO.getId());
if (request.getMac() != null && StringUtils.isNotBlank(request.getMac().getAddress())) {
cmd.setMacAddress(request.getMac().getAddress());
}
userVmService.addNicToVirtualMachine(cmd);
NicVO nic = nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(networkVO.getId(), vmVo.getId());
if (nic == null) {
throw new CloudRuntimeException("Failed to attach NIC to VM");
}
return NicVOToNicConverter.toNic(nic, vmUuid, this::getNetworkById);
} finally {
CallContext.unregister();
}
}
public List<ImageTransfer> listAllImageTransfers() {
List<ImageTransferVO> imageTransfers = imageTransferDao.listAll();
return ImageTransferVOToImageTransferConverter.toImageTransferList(imageTransfers, this::getHostById, this::getVolumeById);
}
public ImageTransfer getImageTransfer(String uuid) {
ImageTransferVO vo = imageTransferDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found");
}
return ImageTransferVOToImageTransferConverter.toImageTransfer(vo, this::getHostById, this::getVolumeById);
}
public ImageTransfer handleCreateImageTransfer(ImageTransfer request) {
if (request == null) {
throw new InvalidParameterValueException("Request image transfer data is empty");
}
if (request.getDisk() == null || StringUtils.isBlank(request.getDisk().id)) {
throw new InvalidParameterValueException("Disk ID must be specified");
}
VolumeJoinVO volumeVO = volumeJoinDao.findByUuid(request.getDisk().id);
if (volumeVO == null) {
throw new InvalidParameterValueException("Disk with ID " + request.getDisk().id + " not found");
}
Direction direction = EnumUtils.fromString(Direction.class, request.getDirection());
if (direction == null) {
throw new InvalidParameterValueException("Invalid or missing direction");
}
return createImageTransfer(null, volumeVO.getId(), direction);
}
public boolean handleCancelImageTransfer(String uuid) {
ImageTransferVO vo = imageTransferDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found");
}
return incrementalBackupService.cancelImageTransfer(vo.getId());
}
public boolean handleFinalizeImageTransfer(String uuid) {
ImageTransferVO vo = imageTransferDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found");
}
return incrementalBackupService.finalizeImageTransfer(vo.getId());
}
private ImageTransfer createImageTransfer(Long backupId, Long volumeId, Direction direction) {
Account serviceAccount = createServiceAccountIfNeeded();
CallContext.register(serviceAccount.getId(), serviceAccount.getId());
try {
org.apache.cloudstack.backup.ImageTransfer imageTransfer =
incrementalBackupService.createImageTransfer(volumeId, null, direction);
ImageTransferVO imageTransferVO = imageTransferDao.findById(imageTransfer.getId());
return ImageTransferVOToImageTransferConverter.toImageTransfer(imageTransferVO, this::getHostById, this::getVolumeById);
} finally {
CallContext.unregister();
}
}
protected DataCenterJoinVO getZoneById(Long zoneId) {
if (zoneId == null) {
return null;
}
return dataCenterJoinDao.findById(zoneId);
}
private HostJoinVO getHostById(Long hostId) {
if (hostId == null) {
return null;
}
return hostJoinDao.findById(hostId);
}
private VolumeJoinVO getVolumeById(Long volumeId) {
if (volumeId == null) {
return null;
}
return volumeJoinDao.findById(volumeId);
}
protected NetworkVO getNetworkById(Long networkId) {
if (networkId == null) {
return null;
}
return networkDao.findById(networkId);
}
public List<Job> listAllJobs() {
return Collections.emptyList();
}
public Job getJob(String uuid) {
// final ClusterVO vo = clusterDao.findByUuid(uuid);
// if (vo == null) {
// throw new InvalidParameterValueException("Cluster with ID " + uuid + " not found");
// }
long startTime = jobsMap.computeIfAbsent(uuid, k -> System.currentTimeMillis());
long elapsed = System.currentTimeMillis() - startTime;
if (elapsed > 10000L) {
return AsyncJobJoinVOToJobConverter.toJob(uuid, "finished", startTime);
} else {
return AsyncJobJoinVOToJobConverter.toJob(uuid, "started", startTime);
}
}
}

View File

@ -1,345 +0,0 @@
// 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.veeam.adapter;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.inject.Inject;
import org.apache.cloudstack.acl.Role;
import org.apache.cloudstack.acl.RolePermissionEntity;
import org.apache.cloudstack.acl.RoleService;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.Rule;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd;
import org.apache.cloudstack.api.command.user.network.ListNetworksCmd;
import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd;
import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
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.volume.AttachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.DeleteVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
import org.apache.cloudstack.backup.ImageTransfer.Direction;
import org.apache.cloudstack.backup.ImageTransferVO;
import org.apache.cloudstack.backup.IncrementalBackupService;
import org.apache.cloudstack.backup.dao.ImageTransferDao;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.veeam.api.converter.ImageTransferVOToImageTransferConverter;
import org.apache.cloudstack.veeam.api.converter.VolumeJoinVOToDiskConverter;
import org.apache.cloudstack.veeam.api.dto.Disk;
import org.apache.cloudstack.veeam.api.dto.ImageTransfer;
import org.apache.cloudstack.veeam.api.dto.Ref;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import com.cloud.api.query.dao.HostJoinDao;
import com.cloud.api.query.dao.VolumeJoinDao;
import com.cloud.api.query.vo.HostJoinVO;
import com.cloud.api.query.vo.VolumeJoinVO;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.org.Grouping;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeApiService;
import com.cloud.storage.dao.VolumeDetailsDao;
import com.cloud.user.Account;
import com.cloud.user.AccountService;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.UserAccount;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.EnumUtils;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.exception.CloudRuntimeException;
public class UserResourceAdapter extends ManagerBase {
private static final String SERVICE_ACCOUNT_NAME = "veemserviceuser";
private static final String SERVICE_ACCOUNT_ROLE_NAME = "Veeam Service Role";
private static final String SERVICE_ACCOUNT_FIRST_NAME = "Veeam";
private static final String SERVICE_ACCOUNT_LAST_NAME = "Service User";
private static final List<Class<?>> SERVICE_ACCOUNT_ROLE_ALLOWED_APIS = Arrays.asList(
QueryAsyncJobResultCmd.class,
ListVMsCmd.class,
DeployVMCmd.class,
StartVMCmd.class,
StopVMCmd.class,
DestroyVMCmd.class,
ListVolumesCmd.class,
CreateVolumeCmd.class,
DeleteVolumeCmd.class,
AttachVolumeCmd.class,
DetachVolumeCmd.class,
ResizeVolumeCmd.class,
ListNetworksCmd.class
);
@Inject
DataCenterDao dataCenterDao;
@Inject
RoleService roleService;
@Inject
AccountService accountService;
@Inject
AccountDao accountDao;
@Inject
VolumeJoinDao volumeJoinDao;
@Inject
VolumeDetailsDao volumeDetailsDao;
@Inject
VolumeApiService volumeApiService;
@Inject
PrimaryDataStoreDao primaryDataStoreDao;
@Inject
ImageTransferDao imageTransferDao;
@Inject
HostJoinDao hostJoinDao;
@Inject
IncrementalBackupService incrementalBackupService;
protected Role createServiceAccountRole() {
Role role = roleService.createRole(SERVICE_ACCOUNT_ROLE_NAME, RoleType.User,
SERVICE_ACCOUNT_ROLE_NAME, false);
for (Class<?> allowedApi : SERVICE_ACCOUNT_ROLE_ALLOWED_APIS) {
final String apiName = BaseCmd.getCommandNameByClass(allowedApi);
roleService.createRolePermission(role, new Rule(apiName), RolePermissionEntity.Permission.ALLOW,
String.format("Allow %s", apiName));
}
roleService.createRolePermission(role, new Rule("*"), RolePermissionEntity.Permission.DENY,
"Deny all");
logger.debug("Created default role for Veeam service account in projects: {}", role);
return role;
}
public Role getServiceAccountRole() {
List<Role> roles = roleService.findRolesByName(SERVICE_ACCOUNT_ROLE_NAME);
if (CollectionUtils.isNotEmpty(roles)) {
Role role = roles.get(0);
logger.debug("Found default role for Veeam service account in projects: {}", role);
return role;
}
return createServiceAccountRole();
}
protected Account createServiceAccount() {
CallContext.register(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM);
try {
Role role = getServiceAccountRole();
UserAccount userAccount = accountService.createUserAccount(SERVICE_ACCOUNT_NAME,
UUID.randomUUID().toString(), SERVICE_ACCOUNT_FIRST_NAME,
SERVICE_ACCOUNT_LAST_NAME, null, null, SERVICE_ACCOUNT_NAME, Account.Type.NORMAL, role.getId(),
1L, null, null, null, null, User.Source.NATIVE);
Account account = accountService.getAccount(userAccount.getAccountId());
logger.debug("Created Veeam service account: {}", account);
return account;
} finally {
CallContext.unregister();
}
}
protected Account createServiceAccountIfNeeded() {
List<AccountVO> accounts = accountDao.findAccountsByName(SERVICE_ACCOUNT_NAME);
for (AccountVO account : accounts) {
if (Account.State.ENABLED.equals(account.getState())) {
logger.debug("Veeam service account found: {}", account);
return account;
}
}
return createServiceAccount();
}
@Override
public boolean start() {
createServiceAccountIfNeeded();
//find public custom disk offering
return true;
}
public List<Disk> listAllDisks() {
List<VolumeJoinVO> kvmVolumes = volumeJoinDao.listByHypervisor(Hypervisor.HypervisorType.KVM);
return VolumeJoinVOToDiskConverter.toDiskList(kvmVolumes);
}
public Disk getDisk(String uuid) {
VolumeJoinVO vo = volumeJoinDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Disk with ID " + uuid + " not found");
}
return VolumeJoinVOToDiskConverter.toDisk(vo);
}
public Disk handleCreateDisk(Disk request) {
if (request == null) {
throw new InvalidParameterValueException("Request disk data is empty");
}
String name = request.name;
if (StringUtils.isBlank(name) && !name.startsWith("Veeam_KvmBackupDisk_")) {
throw new InvalidParameterValueException("Only worker VM disk creation is supported");
}
if (request.storageDomains == null || CollectionUtils.isEmpty(request.storageDomains.storageDomain) ||
request.storageDomains.storageDomain.size() > 1) {
throw new InvalidParameterValueException("Exactly one storage domain must be specified");
}
Ref domain = request.storageDomains.storageDomain.get(0);
if (domain == null || domain.id == null) {
throw new InvalidParameterValueException("Storage domain ID must be specified");
}
StoragePoolVO pool = primaryDataStoreDao.findByUuid(domain.id);
if (pool == null) {
throw new InvalidParameterValueException("Storage domain with ID " + domain.id + " not found");
}
String sizeStr = request.provisionedSize;
if (StringUtils.isBlank(sizeStr)) {
throw new InvalidParameterValueException("Provisioned size must be specified");
}
long provisionedSizeInGb;
try {
provisionedSizeInGb = Long.parseLong(sizeStr);
} catch (NumberFormatException ex) {
throw new InvalidParameterValueException("Invalid provisioned size: " + sizeStr);
}
if (provisionedSizeInGb <= 0) {
throw new InvalidParameterValueException("Provisioned size must be greater than zero");
}
provisionedSizeInGb = Math.max(1L, provisionedSizeInGb / (1024L * 1024L * 1024L));
Long initialSize = null;
if (StringUtils.isNotBlank(request.initialSize)) {
try {
initialSize = Long.parseLong(request.initialSize);
} catch (NumberFormatException ignored) {}
}
Account serviceAccount = createServiceAccountIfNeeded();
DataCenterVO zone = dataCenterDao.findById(pool.getDataCenterId());
if (zone == null || !Grouping.AllocationState.Enabled.equals(zone.getAllocationState())) {
throw new InvalidParameterValueException("Datacenter for the specified storage domain is not found or not active");
}
Long diskOfferingId = volumeApiService.getCustomDiskOfferingIdForVolumeUpload(serviceAccount, zone);
if (diskOfferingId == null) {
throw new CloudRuntimeException("Failed to find custom offering for disk" + zone.getName());
}
CallContext.register(serviceAccount.getId(), serviceAccount.getId());
try {
return createDisk(serviceAccount, pool, name, diskOfferingId, provisionedSizeInGb, initialSize);
} finally {
CallContext.unregister();
}
}
@NotNull
private Disk createDisk(Account serviceAccount, StoragePoolVO pool, String name, Long diskOfferingId, long sizeInGb, Long initialSize) {
Volume volume;
try {
volume = volumeApiService.allocVolume(serviceAccount.getId(), pool.getDataCenterId(), diskOfferingId, null,
null, name, sizeInGb, null, null, null, null);
} catch (ResourceAllocationException e) {
throw new CloudRuntimeException(e.getMessage(), e);
}
if (volume == null) {
throw new CloudRuntimeException("Failed to create volume");
}
volume = volumeApiService.createVolume(volume.getId(), null, null, pool.getId(), true);
if (initialSize != null) {
volumeDetailsDao.addDetail(volume.getId(), ApiConstants.VIRTUAL_SIZE, String.valueOf(initialSize), true);
}
// Implementation for creating a Disk resource
return VolumeJoinVOToDiskConverter.toDisk(volumeJoinDao.findById(volume.getId()));
}
public List<ImageTransfer> listAllImageTransfers() {
List<ImageTransferVO> imageTransfers = imageTransferDao.listAll();
return ImageTransferVOToImageTransferConverter.toImageTransferList(imageTransfers, this::getHostById, this::getVolumeById);
}
private HostJoinVO getHostById(Long hostId) {
if (hostId == null) {
return null;
}
return hostJoinDao.findById(hostId);
}
private VolumeJoinVO getVolumeById(Long volumeId) {
if (volumeId == null) {
return null;
}
return volumeJoinDao.findById(volumeId);
}
public ImageTransfer getImageTransfer(String uuid) {
ImageTransferVO vo = imageTransferDao.findByUuid(uuid);
if (vo == null) {
throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found");
}
return ImageTransferVOToImageTransferConverter.toImageTransfer(vo, this::getHostById, this::getVolumeById);
}
public ImageTransfer handleCreateImageTransfer(ImageTransfer request) {
if (request == null) {
throw new InvalidParameterValueException("Request image transfer data is empty");
}
if (request.getDisk() == null || StringUtils.isBlank(request.getDisk().id)) {
throw new InvalidParameterValueException("Disk ID must be specified");
}
VolumeJoinVO volumeVO = volumeJoinDao.findByUuid(request.getDisk().id);
if (volumeVO == null) {
throw new InvalidParameterValueException("Disk with ID " + request.getDisk().id + " not found");
}
Direction direction = EnumUtils.fromString(Direction.class, request.getDirection());
if (direction == null) {
throw new InvalidParameterValueException("Invalid or missing direction");
}
return createImageTransfer(null, volumeVO.getId(), direction);
}
private ImageTransfer createImageTransfer(Long backupId, Long volumeId, Direction direction) {
Account serviceAccount = createServiceAccountIfNeeded();
CallContext.register(serviceAccount.getId(), serviceAccount.getId());
try {
org.apache.cloudstack.backup.ImageTransfer imageTransfer =
incrementalBackupService.createImageTransfer(volumeId, null, direction);
ImageTransferVO imageTransferVO = imageTransferDao.findById(imageTransfer.getId());
return ImageTransferVOToImageTransferConverter.toImageTransfer(imageTransferVO, this::getHostById, this::getVolumeById);
} finally {
CallContext.unregister();
}
}
}

View File

@ -32,13 +32,13 @@ import org.apache.cloudstack.veeam.RouteHandler;
import org.apache.cloudstack.veeam.VeeamControlService;
import org.apache.cloudstack.veeam.VeeamControlServlet;
import org.apache.cloudstack.veeam.api.dto.Api;
import org.apache.cloudstack.veeam.api.dto.ApiSummary;
import org.apache.cloudstack.veeam.api.dto.EmptyElement;
import org.apache.cloudstack.veeam.api.dto.Link;
import org.apache.cloudstack.veeam.api.dto.ProductInfo;
import org.apache.cloudstack.veeam.api.dto.Ref;
import org.apache.cloudstack.veeam.api.dto.SpecialObjectRef;
import org.apache.cloudstack.veeam.api.dto.SpecialObjects;
import org.apache.cloudstack.veeam.api.dto.ApiSummary;
import org.apache.cloudstack.veeam.api.dto.SummaryCount;
import org.apache.cloudstack.veeam.api.dto.Version;
import org.apache.cloudstack.veeam.utils.Negotiation;
@ -65,7 +65,7 @@ public class ApiService extends ManagerBase implements RouteHandler {
}
private void handleRootApiRequest(HttpServletRequest req, HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
io.getWriter().write(resp, 200,
io.getWriter().write(resp, HttpServletResponse.SC_OK,
createDummyApi(VeeamControlService.ContextPath.value() + BASE_ROUTE),
outFormat);
}

View File

@ -26,27 +26,21 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.cloudstack.veeam.RouteHandler;
import org.apache.cloudstack.veeam.VeeamControlServlet;
import org.apache.cloudstack.veeam.api.converter.ClusterVOToClusterConverter;
import org.apache.cloudstack.veeam.adapter.ServerAdapter;
import org.apache.cloudstack.veeam.api.dto.Cluster;
import org.apache.cloudstack.veeam.api.dto.Clusters;
import org.apache.cloudstack.veeam.utils.Negotiation;
import org.apache.cloudstack.veeam.utils.PathUtil;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.api.query.dao.DataCenterJoinDao;
import com.cloud.api.query.vo.DataCenterJoinVO;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.component.ManagerBase;
public class ClustersRouteHandler extends ManagerBase implements RouteHandler {
public static final String BASE_ROUTE = "/api/clusters";
@Inject
ClusterDao clusterDao;
@Inject
DataCenterJoinDao dataCenterJoinDao;
ServerAdapter serverAdapter;
@Override
public boolean start() {
@ -90,32 +84,19 @@ public class ClustersRouteHandler extends ManagerBase implements RouteHandler {
protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
final List<Cluster> result = ClusterVOToClusterConverter.toClusterList(listClusters(), this::getZoneById);
final List<Cluster> result = serverAdapter.listAllClusters();
final Clusters response = new Clusters(result);
io.getWriter().write(resp, 200, response, outFormat);
}
protected List<ClusterVO> listClusters() {
return clusterDao.listAll();
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
}
protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
final ClusterVO vo = clusterDao.findByUuid(id);
if (vo == null) {
io.notFound(resp, "DataCenter not found: " + id, outFormat);
return;
try {
Cluster response = serverAdapter.getCluster(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
Cluster response = ClusterVOToClusterConverter.toCluster(vo, this::getZoneById);
io.getWriter().write(resp, 200, response, outFormat);
}
protected DataCenterJoinVO getZoneById(Long zoneId) {
if (zoneId == null) {
return null;
}
return dataCenterJoinDao.findById(zoneId);
}
}

View File

@ -26,9 +26,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.cloudstack.veeam.RouteHandler;
import org.apache.cloudstack.veeam.VeeamControlServlet;
import org.apache.cloudstack.veeam.api.converter.DataCenterJoinVOToDataCenterConverter;
import org.apache.cloudstack.veeam.api.converter.NetworkVOToNetworkConverter;
import org.apache.cloudstack.veeam.api.converter.StoreVOToStorageDomainConverter;
import org.apache.cloudstack.veeam.adapter.ServerAdapter;
import org.apache.cloudstack.veeam.api.dto.DataCenter;
import org.apache.cloudstack.veeam.api.dto.DataCenters;
import org.apache.cloudstack.veeam.api.dto.Network;
@ -39,33 +37,14 @@ import org.apache.cloudstack.veeam.utils.Negotiation;
import org.apache.cloudstack.veeam.utils.PathUtil;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.api.query.dao.DataCenterJoinDao;
import com.cloud.api.query.dao.ImageStoreJoinDao;
import com.cloud.api.query.dao.StoragePoolJoinDao;
import com.cloud.api.query.vo.DataCenterJoinVO;
import com.cloud.api.query.vo.ImageStoreJoinVO;
import com.cloud.api.query.vo.StoragePoolJoinVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.component.ManagerBase;
public class DataCentersRouteHandler extends ManagerBase implements RouteHandler {
public static final String BASE_ROUTE = "/api/datacenters";
private static final int DEFAULT_MAX = 50;
private static final int HARD_CAP_MAX = 1000;
private static final int DEFAULT_PAGE = 1;
@Inject
DataCenterJoinDao dataCenterJoinDao;
@Inject
StoragePoolJoinDao storagePoolJoinDao;
@Inject
ImageStoreJoinDao imageStoreJoinDao;
@Inject
NetworkDao networkDao;
ServerAdapter serverAdapter;
@Override
public boolean start() {
@ -119,66 +98,41 @@ public class DataCentersRouteHandler extends ManagerBase implements RouteHandler
protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
final List<DataCenter> result = DataCenterJoinVOToDataCenterConverter.toDCList(listDCs());
final List<DataCenter> result = serverAdapter.listAllDataCenters();
final DataCenters response = new DataCenters(result);
io.getWriter().write(resp, 200, response, outFormat);
}
protected List<DataCenterJoinVO> listDCs() {
return dataCenterJoinDao.listAll();
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
}
protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(id);
if (dataCenterVO == null) {
io.notFound(resp, "DataCenter not found: " + id, outFormat);
return;
try {
DataCenter response = serverAdapter.getDataCenter(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
DataCenter response = DataCenterJoinVOToDataCenterConverter.toDataCenter(dataCenterVO);
io.getWriter().write(resp, 200, response, outFormat);
}
protected List<StoragePoolJoinVO> listStoragePoolsByDcId(final long dcId) {
return storagePoolJoinDao.listAll();
}
protected List<ImageStoreJoinVO> listImageStoresByDcId(final long dcId) {
return imageStoreJoinDao.listAll();
}
protected List<NetworkVO> listNetworksByDcId(final long dcId) {
return networkDao.listAll();
}
protected void handleGetStorageDomainsByDcId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(id);
if (dataCenterVO == null) {
io.notFound(resp, "DataCenter not found: " + id, outFormat);
return;
try {
List<StorageDomain> storageDomains = serverAdapter.listStorageDomainsByDcId(id);
StorageDomains response = new StorageDomains(storageDomains);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
List<StorageDomain> storageDomains = StoreVOToStorageDomainConverter.toStorageDomainListFromPools(listStoragePoolsByDcId(dataCenterVO.getId()));
storageDomains.addAll(StoreVOToStorageDomainConverter.toStorageDomainListFromStores(listImageStoresByDcId(dataCenterVO.getId())));
StorageDomains response = new StorageDomains(storageDomains);
io.getWriter().write(resp, 200, response, outFormat);
}
protected void handleGetNetworksByDcId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(id);
if (dataCenterVO == null) {
io.notFound(resp, "DataCenter not found: " + id, outFormat);
return;
try {
List<Network> networks = serverAdapter.listNetworksByDcId(id);
Networks response = new Networks(networks);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
List<Network> networks = NetworkVOToNetworkConverter.toNetworkList(listNetworksByDcId(dataCenterVO.getId()), (dcId) -> dataCenterVO);
Networks response = new Networks(networks);
io.getWriter().write(resp, 200, response, outFormat);
}
}

View File

@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.cloudstack.veeam.RouteHandler;
import org.apache.cloudstack.veeam.VeeamControlServlet;
import org.apache.cloudstack.veeam.adapter.UserResourceAdapter;
import org.apache.cloudstack.veeam.adapter.ServerAdapter;
import org.apache.cloudstack.veeam.api.dto.Disk;
import org.apache.cloudstack.veeam.api.dto.Disks;
import org.apache.cloudstack.veeam.utils.Negotiation;
@ -42,7 +42,7 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler {
public static final String BASE_ROUTE = "/api/disks";
@Inject
UserResourceAdapter userResourceAdapter;
ServerAdapter serverAdapter;
@Override
public boolean start() {
@ -74,16 +74,22 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler {
}
}
if (!"GET".equalsIgnoreCase(method)) {
io.methodNotAllowed(resp, "GET", outFormat);
return;
}
List<String> idAndSubPath = PathUtil.extractIdAndSubPath(sanitizedPath, BASE_ROUTE);
if (CollectionUtils.isNotEmpty(idAndSubPath)) {
String id = idAndSubPath.get(0);
if (idAndSubPath.size() == 1) {
handleGetById(id, resp, outFormat, io);
return;
if (!"GET".equalsIgnoreCase(method) && !"DELETE".equalsIgnoreCase(method)) {
io.methodNotAllowed(resp, "GET, DELETE", outFormat);
return;
}
if ("GET".equalsIgnoreCase(method)) {
handleGetById(id, resp, outFormat, io);
return;
}
if ("DELETE".equalsIgnoreCase(method)) {
handleDeleteById(id, resp, outFormat, io);
return;
}
}
}
@ -92,32 +98,42 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler {
protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
final List<Disk> result = userResourceAdapter.listAllDisks();
final List<Disk> result = serverAdapter.listAllDisks();
final Disks response = new Disks(result);
io.getWriter().write(resp, 200, response, outFormat);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
}
protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp,
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
String data = RouteHandler.getRequestData(req);
logger.info("Received POST request on /api/disks endpoint, but method: POST is not supported atm. Request-data: {}", data);
logger.info("Received POST request on /api/disks endpoint. Request-data: {}", data); // ToDo: remove
try {
Disk request = io.getMapper().jsonMapper().readValue(data, Disk.class);
Disk response = userResourceAdapter.handleCreateDisk(request);
io.getWriter().write(resp, 201, response, outFormat);
Disk response = serverAdapter.handleCreateDisk(request);
io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat);
} catch (JsonProcessingException | CloudRuntimeException e) {
io.getWriter().write(resp, 400, e.getMessage(), outFormat);
io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad request", e.getMessage(), outFormat);
}
}
protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
try {
Disk response = userResourceAdapter.getDisk(id);
io.getWriter().write(resp, 200, response, outFormat);
Disk response = serverAdapter.getDisk(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().write(resp, 404, e.getMessage(), outFormat);
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
}
protected void handleDeleteById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
try {
serverAdapter.deleteDisk(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, "Deleted disk ID: " + id, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad request", e.getMessage(), outFormat);
}
}
}

View File

@ -26,22 +26,21 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.cloudstack.veeam.RouteHandler;
import org.apache.cloudstack.veeam.VeeamControlServlet;
import org.apache.cloudstack.veeam.api.converter.HostJoinVOToHostConverter;
import org.apache.cloudstack.veeam.adapter.ServerAdapter;
import org.apache.cloudstack.veeam.api.dto.Host;
import org.apache.cloudstack.veeam.api.dto.Hosts;
import org.apache.cloudstack.veeam.utils.Negotiation;
import org.apache.cloudstack.veeam.utils.PathUtil;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.api.query.dao.HostJoinDao;
import com.cloud.api.query.vo.HostJoinVO;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.component.ManagerBase;
public class HostsRouteHandler extends ManagerBase implements RouteHandler {
public static final String BASE_ROUTE = "/api/hosts";
@Inject
HostJoinDao hostJoinDao;
ServerAdapter serverAdapter;
@Override
public boolean start() {
@ -85,25 +84,19 @@ public class HostsRouteHandler extends ManagerBase implements RouteHandler {
protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
final List<Host> result = HostJoinVOToHostConverter.toHostList(listHosts());
final List<Host> result = serverAdapter.listAllHosts();
final Hosts response = new Hosts(result);
io.getWriter().write(resp, 200, response, outFormat);
}
protected List<HostJoinVO> listHosts() {
return hostJoinDao.listAll();
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
}
protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
final HostJoinVO vo = hostJoinDao.findByUuid(id);
if (vo == null) {
io.notFound(resp, "DataCenter not found: " + id, outFormat);
return;
try {
Host response = serverAdapter.getHost(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
Host response = HostJoinVOToHostConverter.toHost(vo);
io.getWriter().write(resp, 200, response, outFormat);
}
}

View File

@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.cloudstack.veeam.RouteHandler;
import org.apache.cloudstack.veeam.VeeamControlServlet;
import org.apache.cloudstack.veeam.adapter.UserResourceAdapter;
import org.apache.cloudstack.veeam.adapter.ServerAdapter;
import org.apache.cloudstack.veeam.api.dto.ImageTransfer;
import org.apache.cloudstack.veeam.api.dto.ImageTransfers;
import org.apache.cloudstack.veeam.utils.Negotiation;
@ -42,7 +42,7 @@ public class ImageTransfersRouteHandler extends ManagerBase implements RouteHand
public static final String BASE_ROUTE = "/api/imagetransfers";
@Inject
UserResourceAdapter userResourceAdapter;
ServerAdapter serverAdapter;
@Override
public boolean start() {
@ -105,11 +105,10 @@ public class ImageTransfersRouteHandler extends ManagerBase implements RouteHand
protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
final List<ImageTransfer> result = userResourceAdapter.listAllImageTransfers();
final List<ImageTransfer> result = serverAdapter.listAllImageTransfers();
final ImageTransfers response = new ImageTransfers();
response.setImageTransfer(result);
io.getWriter().write(resp, 400, response, outFormat);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
}
protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp,
@ -118,32 +117,40 @@ public class ImageTransfersRouteHandler extends ManagerBase implements RouteHand
logger.info("Received POST request on /api/imagetransfers endpoint. Request-data: {}", data);
try {
ImageTransfer request = io.getMapper().jsonMapper().readValue(data, ImageTransfer.class);
ImageTransfer response = userResourceAdapter.handleCreateImageTransfer(request);
io.getWriter().write(resp, 201, response, outFormat);
ImageTransfer response = serverAdapter.handleCreateImageTransfer(request);
io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat);
} catch (JsonProcessingException | CloudRuntimeException e) {
io.getWriter().write(resp, 400, e.getMessage(), outFormat);
io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad Request", e.getMessage(), outFormat);
}
}
protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
try {
ImageTransfer response = userResourceAdapter.getImageTransfer(id);
io.getWriter().write(resp, 200, response, outFormat);
ImageTransfer response = serverAdapter.getImageTransfer(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().write(resp, 404, e.getMessage(), outFormat);
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
}
protected void handleCancelById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
//ToDo: implement cancel logic
io.getWriter().write(resp, 200, "Image transfer cancelled successfully", outFormat);
try {
serverAdapter.handleCancelImageTransfer(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, "Image transfer cancelled successfully", outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
}
protected void handleFinalizeById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
//ToDo: implement finalize logic
io.getWriter().write(resp, 200, "Image transfer finalized successfully", outFormat);
try {
serverAdapter.handleFinalizeImageTransfer(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, "Image transfer finalized successfully", outFormat);
} catch (CloudRuntimeException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
}
}

View File

@ -0,0 +1,102 @@
// 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.veeam.api;
import java.io.IOException;
import java.util.List;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.cloudstack.veeam.RouteHandler;
import org.apache.cloudstack.veeam.VeeamControlServlet;
import org.apache.cloudstack.veeam.adapter.ServerAdapter;
import org.apache.cloudstack.veeam.api.dto.Job;
import org.apache.cloudstack.veeam.api.dto.Jobs;
import org.apache.cloudstack.veeam.utils.Negotiation;
import org.apache.cloudstack.veeam.utils.PathUtil;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.component.ManagerBase;
public class JobsRouteHandler extends ManagerBase implements RouteHandler {
public static final String BASE_ROUTE = "/api/jobs";
@Inject
ServerAdapter serverAdapter;
@Override
public boolean start() {
return true;
}
@Override
public int priority() {
return 5;
}
@Override
public boolean canHandle(String method, String path) {
return getSanitizedPath(path).startsWith(BASE_ROUTE);
}
@Override
public void handle(HttpServletRequest req, HttpServletResponse resp, String path, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
final String method = req.getMethod();
if (!"GET".equalsIgnoreCase(method)) {
io.methodNotAllowed(resp, "GET", outFormat);
return;
}
final String sanitizedPath = getSanitizedPath(path);
if (sanitizedPath.equals(BASE_ROUTE)) {
handleGet(req, resp, outFormat, io);
return;
}
List<String> idAndSubPath = PathUtil.extractIdAndSubPath(sanitizedPath, BASE_ROUTE);
if (CollectionUtils.isNotEmpty(idAndSubPath)) {
String id = idAndSubPath.get(0);
if (idAndSubPath.size() == 1) {
handleGetById(id, resp, outFormat, io);
return;
}
}
resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Not found");
}
protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
final List<Job> result = serverAdapter.listAllJobs();
final Jobs response = new Jobs(result);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
}
protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
try {
Job response = serverAdapter.getJob(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
}
}

View File

@ -26,27 +26,21 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.cloudstack.veeam.RouteHandler;
import org.apache.cloudstack.veeam.VeeamControlServlet;
import org.apache.cloudstack.veeam.api.converter.NetworkVOToNetworkConverter;
import org.apache.cloudstack.veeam.adapter.ServerAdapter;
import org.apache.cloudstack.veeam.api.dto.Network;
import org.apache.cloudstack.veeam.api.dto.Networks;
import org.apache.cloudstack.veeam.utils.Negotiation;
import org.apache.cloudstack.veeam.utils.PathUtil;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.api.query.dao.DataCenterJoinDao;
import com.cloud.api.query.vo.DataCenterJoinVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.component.ManagerBase;
public class NetworksRouteHandler extends ManagerBase implements RouteHandler {
public static final String BASE_ROUTE = "/api/networks";
@Inject
NetworkDao networkDao;
@Inject
DataCenterJoinDao dataCenterJoinDao;
ServerAdapter serverAdapter;
@Override
public boolean start() {
@ -90,32 +84,19 @@ public class NetworksRouteHandler extends ManagerBase implements RouteHandler {
protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
final List<Network> result = NetworkVOToNetworkConverter.toNetworkList(listNetworks(), this::getZoneById);
final List<Network> result = serverAdapter.listAllNetworks();
final Networks response = new Networks(result);
io.getWriter().write(resp, 200, response, outFormat);
}
protected List<NetworkVO> listNetworks() {
return networkDao.listAll();
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
}
protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
final NetworkVO vo = networkDao.findByUuid(id);
if (vo == null) {
io.notFound(resp, "DataCenter not found: " + id, outFormat);
return;
try {
Network response = serverAdapter.getNetwork(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
Network response = NetworkVOToNetworkConverter.toNetwork(vo, this::getZoneById);
io.getWriter().write(resp, 200, response, outFormat);
}
protected DataCenterJoinVO getZoneById(Long zoneId) {
if (zoneId == null) {
return null;
}
return dataCenterJoinDao.findById(zoneId);
}
}

View File

@ -27,27 +27,26 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.cloudstack.veeam.RouteHandler;
import org.apache.cloudstack.veeam.VeeamControlServlet;
import org.apache.cloudstack.veeam.api.converter.UserVmJoinVOToVmConverter;
import org.apache.cloudstack.veeam.api.converter.VolumeJoinVOToDiskConverter;
import org.apache.cloudstack.veeam.adapter.ServerAdapter;
import org.apache.cloudstack.veeam.api.dto.DiskAttachment;
import org.apache.cloudstack.veeam.api.dto.DiskAttachments;
import org.apache.cloudstack.veeam.api.dto.Nic;
import org.apache.cloudstack.veeam.api.dto.Nics;
import org.apache.cloudstack.veeam.api.dto.Vm;
import org.apache.cloudstack.veeam.api.dto.VmAction;
import org.apache.cloudstack.veeam.api.dto.Vms;
import org.apache.cloudstack.veeam.api.request.VmListQuery;
import org.apache.cloudstack.veeam.api.request.VmSearchExpr;
import org.apache.cloudstack.veeam.api.request.VmSearchFilters;
import org.apache.cloudstack.veeam.api.request.VmSearchParser;
import org.apache.cloudstack.veeam.api.response.VmCollectionResponse;
import org.apache.cloudstack.veeam.api.response.VmEntityResponse;
import org.apache.cloudstack.veeam.utils.Negotiation;
import org.apache.cloudstack.veeam.utils.PathUtil;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.api.query.dao.HostJoinDao;
import com.cloud.api.query.dao.UserVmJoinDao;
import com.cloud.api.query.dao.VolumeJoinDao;
import com.cloud.api.query.vo.HostJoinVO;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.exception.CloudRuntimeException;
import com.fasterxml.jackson.core.JsonProcessingException;
public class VmsRouteHandler extends ManagerBase implements RouteHandler {
public static final String BASE_ROUTE = "/api/vms";
@ -56,13 +55,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
private static final int DEFAULT_PAGE = 1;
@Inject
UserVmJoinDao userVmJoinDao;
@Inject
HostJoinDao hostJoinDao;
@Inject
VolumeJoinDao volumeJoinDao;
ServerAdapter serverAdapter;
private VmSearchParser searchParser;
@ -90,24 +83,74 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
final String method = req.getMethod();
final String sanitizedPath = getSanitizedPath(path);
if (sanitizedPath.equals(BASE_ROUTE)) {
if (!"GET".equalsIgnoreCase(method)) {
io.methodNotAllowed(resp, "GET", outFormat);
if (!"GET".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method) && !"DELETE".equalsIgnoreCase(method)) {
io.methodNotAllowed(resp, "GET, POST, DELETE", outFormat);
return;
}
if ("GET".equalsIgnoreCase(method)) {
handleGet(req, resp, outFormat, io);
return;
}
if ("POST".equalsIgnoreCase(method)) {
handlePost(req, resp, outFormat, io);
return;
}
handleGet(req, resp, outFormat, io);
return;
}
List<String> idAndSubPath = PathUtil.extractIdAndSubPath(sanitizedPath, BASE_ROUTE);
if (CollectionUtils.isNotEmpty(idAndSubPath)) {
String id = idAndSubPath.get(0);
if (idAndSubPath.size() == 1) {
handleGetById(id, resp, outFormat, io);
if (!"GET".equalsIgnoreCase(method) && !"PUT".equalsIgnoreCase(method) && !"DELETE".equalsIgnoreCase(method)) {
io.methodNotAllowed(resp, "GET, PUT, DELETE", outFormat);
} else if ("GET".equalsIgnoreCase(method)) {
handleGetById(id, resp, outFormat, io);
} else if ("DELETE".equalsIgnoreCase(method)) {
handleUpdateById(id, req, resp, outFormat, io);
} else if ("DELETE".equalsIgnoreCase(method)) {
handleDeleteById(id, resp, outFormat, io);
}
return;
} else if (idAndSubPath.size() == 2) {
String subPath = idAndSubPath.get(1);
if ("diskattachments".equals(subPath)) {
handleGetDisAttachmentsByVmId(id, resp, outFormat, io);
if ("start".equals(subPath)) {
if ("POST".equalsIgnoreCase(method)) {
handleStartVmById(id, req, resp, outFormat, io);
} else {
io.methodNotAllowed(resp, "POST", outFormat);
}
return;
} else if ("stop".equals(subPath)) {
if ("POST".equalsIgnoreCase(method)) {
handleStopVmById(id, req, resp, outFormat, io);
} else {
io.methodNotAllowed(resp, "POST", outFormat);
}
return;
} else if ("shutdown".equals(subPath)) {
if ("POST".equalsIgnoreCase(method)) {
handleShutdownVmById(id, req, resp, outFormat, io);
} else {
io.methodNotAllowed(resp, "POST", outFormat);
}
return;
} else if ("diskattachments".equals(subPath)) {
if (!"GET".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method)) {
io.methodNotAllowed(resp, "GET, POST", outFormat);
} else if ("GET".equalsIgnoreCase(method)) {
handleGetDiskAttachmentsByVmId(id, resp, outFormat, io);
} else if ("POST".equalsIgnoreCase(method)) {
handlePostDiskAttachmentForVmId(id, req, resp, outFormat, io);
}
return;
} else if ("nics".equals(subPath)) {
if (!"GET".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method)) {
io.methodNotAllowed(resp, "GET, POST", outFormat);
} else if ("GET".equalsIgnoreCase(method)) {
handleGetNicsByVmId(id, resp, outFormat, io);
} else if ("POST".equalsIgnoreCase(method)) {
handlePostNicForVmId(id, req, resp, outFormat, io);
}
return;
}
}
@ -149,10 +192,10 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
return;
}
final List<Vm> result = UserVmJoinVOToVmConverter.toVmList(listUserVms(), this::getHostById);
final VmCollectionResponse response = new VmCollectionResponse(result);
final List<Vm> result = serverAdapter.listAllUserVms();
final Vms response = new Vms(result);
io.getWriter().write(resp, 200, response, outFormat);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
}
protected static VmListQuery fromRequest(final HttpServletRequest req) {
@ -172,41 +215,123 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
}
}
protected List<UserVmJoinVO> listUserVms() {
// Todo: add filtering, pagination
return userVmJoinDao.listAll();
protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp,
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
String data = RouteHandler.getRequestData(req);
logger.info("Received method: POST request. Request-data: {}", data);
try {
Vm request = io.getMapper().jsonMapper().readValue(data, Vm.class);
Vm response = serverAdapter.handleCreateVm(request);
io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat);
} catch (JsonProcessingException | CloudRuntimeException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad request", e.getMessage(), outFormat);
}
}
protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
final UserVmJoinVO userVmJoinVO = userVmJoinDao.findByUuid(id);
if (userVmJoinVO == null) {
io.notFound(resp, "VM not found: " + id, outFormat);
return;
try {
Vm response = serverAdapter.getVm(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
VmEntityResponse response = new VmEntityResponse(UserVmJoinVOToVmConverter.toVm(userVmJoinVO, this::getHostById));
io.getWriter().write(resp, 200, response, outFormat);
}
protected void handleGetDisAttachmentsByVmId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
final UserVmJoinVO userVmJoinVO = userVmJoinDao.findByUuid(id);
if (userVmJoinVO == null) {
io.notFound(resp, "VM not found: " + id, outFormat);
return;
}
List<DiskAttachment> disks = VolumeJoinVOToDiskConverter.toDiskAttachmentList(
volumeJoinDao.listByInstanceId(userVmJoinVO.getId()));
DiskAttachments response = new DiskAttachments(disks);
io.getWriter().write(resp, 200, response, outFormat);
protected void handleUpdateById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
String data = RouteHandler.getRequestData(req);
logger.info("Received POST request, but method: POST is not supported atm. Request-data: {}", data);
io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Not implemented", "", outFormat);
}
protected HostJoinVO getHostById(Long hostId) {
if (hostId == null) {
return null;
protected void handleDeleteById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
try {
serverAdapter.deleteVm(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, "", outFormat);
} catch (CloudRuntimeException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
}
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.startVm(id);
io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat);
} catch (CloudRuntimeException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
}
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.stopVm(id);
io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat);
} catch (CloudRuntimeException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
}
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.shutdownVm(id);
io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat);
} catch (CloudRuntimeException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
}
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);
DiskAttachments response = new DiskAttachments(disks);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
}
protected void handlePostDiskAttachmentForVmId(final String id, final HttpServletRequest req,
final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io)
throws IOException {
String data = RouteHandler.getRequestData(req);
logger.info("Received method: POST request. Request-data: {}", data);
try {
DiskAttachment request = io.getMapper().jsonMapper().readValue(data, DiskAttachment.class);
DiskAttachment response = serverAdapter.handleVmAttachDisk(id, request);
io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat);
} catch (JsonProcessingException | CloudRuntimeException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad request", e.getMessage(), outFormat);
}
}
protected void handleGetNicsByVmId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
try {
List<Nic> nics = serverAdapter.listNicsByInstanceId(id);
Nics response = new Nics(nics);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
}
protected void handlePostNicForVmId(final String id, final HttpServletRequest req,
final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io)
throws IOException {
String data = RouteHandler.getRequestData(req);
logger.info("Received method: POST request. Request-data: {}", data);
try {
Nic request = io.getMapper().jsonMapper().readValue(data, Nic.class);
Nic response = serverAdapter.handleVmAttachNic(id, request);
io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat);
} catch (JsonProcessingException | CloudRuntimeException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad request", e.getMessage(), outFormat);
}
return hostJoinDao.findById(hostId);
}
}

View File

@ -26,27 +26,21 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.cloudstack.veeam.RouteHandler;
import org.apache.cloudstack.veeam.VeeamControlServlet;
import org.apache.cloudstack.veeam.api.converter.NetworkVOToVnicProfileConverter;
import org.apache.cloudstack.veeam.adapter.ServerAdapter;
import org.apache.cloudstack.veeam.api.dto.VnicProfile;
import org.apache.cloudstack.veeam.api.dto.VnicProfiles;
import org.apache.cloudstack.veeam.utils.Negotiation;
import org.apache.cloudstack.veeam.utils.PathUtil;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.api.query.dao.DataCenterJoinDao;
import com.cloud.api.query.vo.DataCenterJoinVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.component.ManagerBase;
public class VnicProfilesRouteHandler extends ManagerBase implements RouteHandler {
public static final String BASE_ROUTE = "/api/vnicprofiles";
@Inject
NetworkDao networkDao;
@Inject
DataCenterJoinDao dataCenterJoinDao;
ServerAdapter serverAdapter;
@Override
public boolean start() {
@ -90,32 +84,19 @@ public class VnicProfilesRouteHandler extends ManagerBase implements RouteHandle
protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
final List<VnicProfile> result = NetworkVOToVnicProfileConverter.toVnicProfileList(listNetworks(), this::getZoneById);
final List<VnicProfile> result = serverAdapter.listAllVnicProfiles();
final VnicProfiles response = new VnicProfiles(result);
io.getWriter().write(resp, 200, response, outFormat);
}
protected List<NetworkVO> listNetworks() {
return networkDao.listAll();
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
}
protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
final NetworkVO vo = networkDao.findByUuid(id);
if (vo == null) {
io.notFound(resp, "DataCenter not found: " + id, outFormat);
return;
try {
VnicProfile response = serverAdapter.getVnicProfile(id);
io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat);
} catch (InvalidParameterValueException e) {
io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat);
}
VnicProfile response = NetworkVOToVnicProfileConverter.toVnicProfile(vo, this::getZoneById);
io.getWriter().write(resp, 200, response, outFormat);
}
protected DataCenterJoinVO getZoneById(Long zoneId) {
if (zoneId == null) {
return null;
}
return dataCenterJoinDao.findById(zoneId);
}
}

View File

@ -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
// 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.veeam.api.converter;
import java.util.Collections;
import org.apache.cloudstack.veeam.VeeamControlService;
import org.apache.cloudstack.veeam.api.JobsRouteHandler;
import org.apache.cloudstack.veeam.api.dto.Actions;
import org.apache.cloudstack.veeam.api.dto.Job;
import org.apache.cloudstack.veeam.api.dto.Ref;
public class AsyncJobJoinVOToJobConverter {
public static Job toJob(String uuid, String state, long startTime) {
Job job = new Job();
final String basePath = VeeamControlService.ContextPath.value();
// Fill in dummy data for now, as the AsyncJobJoinVO does not contain all the necessary information to populate a Job object.
job.setId(uuid);
job.setHref(basePath + JobsRouteHandler.BASE_ROUTE + "/" + uuid);
job.setAutoCleared(Boolean.TRUE.toString());
job.setExternal(Boolean.TRUE.toString());
job.setLastUpdated(System.currentTimeMillis());
job.setStartTime(startTime);
job.setStatus(state);
if ("complete".equalsIgnoreCase(state) || "finished".equalsIgnoreCase(state)) {
job.setEndTime(System.currentTimeMillis());
}
job.setOwner(Ref.of(basePath + "/api/users/" + uuid, uuid));
job.setActions(new Actions());
job.setDescription("Something");
job.setLink(Collections.emptyList());
return job;
}
}

View File

@ -0,0 +1,94 @@
// 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.veeam.api.converter;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.cloudstack.veeam.VeeamControlService;
import org.apache.cloudstack.veeam.api.VnicProfilesRouteHandler;
import org.apache.cloudstack.veeam.api.dto.Ip;
import org.apache.cloudstack.veeam.api.dto.Ips;
import org.apache.cloudstack.veeam.api.dto.Mac;
import org.apache.cloudstack.veeam.api.dto.Nic;
import org.apache.cloudstack.veeam.api.dto.Ref;
import org.apache.cloudstack.veeam.api.dto.ReportedDevice;
import org.apache.cloudstack.veeam.api.dto.ReportedDevices;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import com.cloud.network.dao.NetworkVO;
import com.cloud.vm.NicVO;
public class NicVOToNicConverter {
public static Nic toNic(final NicVO vo, final String vmUuid, final Function<Long, NetworkVO> networkResolver) {
final String basePath = VeeamControlService.ContextPath.value();
final Nic nic = new Nic();
nic.setId(vo.getUuid());
nic.setName(vo.getReserver());
Mac mac = new Mac();
mac.setAddress(vo.getMacAddress());
nic.setMac(mac);
nic.setLinked(true);
nic.setPlugged(true);
if (StringUtils.isBlank(vmUuid)) {
nic.setVm(Ref.of(basePath + "/vms/" + vmUuid, vmUuid));
nic.setHref(nic.getVm().href + "/nics/" + vo.getUuid());
}
nic.setInterfaceType("virtio");
ReportedDevice device = getReportedDevice(vo, mac, nic.getVm());
nic.setReportedDevices(new ReportedDevices(List.of(device)));
if (networkResolver != null) {
final NetworkVO network = networkResolver.apply(vo.getNetworkId());
if (network != null) {
nic.setVnicProfile(Ref.of(basePath + VnicProfilesRouteHandler.BASE_ROUTE + "/" + network.getUuid(), network.getUuid()));
}
}
return nic;
}
@NotNull
private static ReportedDevice getReportedDevice(NicVO vo, Mac mac, Ref vm) {
ReportedDevice device = new ReportedDevice();
device.setType("network");
device.setId(vo.getUuid());
device.setName("eth0");
device.setMac(mac);
Ip ip = new Ip();
if (vo.getIPv4Address() != null) {
ip.setAddress(vo.getIPv4Address());
ip.setGateway(vo.getIPv4Gateway());
ip.setVersion("v4");
} else if (vo.getIPv6Address() != null) {
ip.setAddress(vo.getIPv6Address());
ip.setGateway(vo.getIPv6Gateway());
ip.setVersion("v6");
}
device.setIps(new Ips(List.of(ip)));
device.setVm(vm);
return device;
}
public static List<Nic> toNicList(final List<NicVO> vos, final String vmUuid, final Function<Long, NetworkVO> networkResolver) {
return vos.stream()
.map(vo -> toNic(vo, vmUuid, networkResolver))
.collect(Collectors.toList());
}
}

View File

@ -25,14 +25,22 @@ import java.util.stream.Collectors;
import org.apache.cloudstack.veeam.VeeamControlService;
import org.apache.cloudstack.veeam.api.ApiService;
import org.apache.cloudstack.veeam.api.JobsRouteHandler;
import org.apache.cloudstack.veeam.api.VmsRouteHandler;
import org.apache.cloudstack.veeam.api.dto.Actions;
import org.apache.cloudstack.veeam.api.dto.Bios;
import org.apache.cloudstack.veeam.api.dto.Cpu;
import org.apache.cloudstack.veeam.api.dto.DiskAttachment;
import org.apache.cloudstack.veeam.api.dto.DiskAttachments;
import org.apache.cloudstack.veeam.api.dto.EmptyElement;
import org.apache.cloudstack.veeam.api.dto.Link;
import org.apache.cloudstack.veeam.api.dto.Nic;
import org.apache.cloudstack.veeam.api.dto.Nics;
import org.apache.cloudstack.veeam.api.dto.Os;
import org.apache.cloudstack.veeam.api.dto.Ref;
import org.apache.cloudstack.veeam.api.dto.Topology;
import org.apache.cloudstack.veeam.api.dto.Vm;
import org.apache.cloudstack.veeam.api.dto.VmAction;
import org.apache.commons.lang3.StringUtils;
import com.cloud.api.query.vo.HostJoinVO;
@ -49,7 +57,8 @@ public final class UserVmJoinVOToVmConverter {
*
* @param src UserVmJoinVO
*/
public static Vm toVm(final UserVmJoinVO src, final Function<Long, HostJoinVO> hostResolver) {
public static Vm toVm(final UserVmJoinVO src, final Function<Long, HostJoinVO> hostResolver,
final Function<Long, List<DiskAttachment>> disksResolver, final Function<UserVmJoinVO, List<Nic>> nicsResolver) {
if (src == null) {
return null;
}
@ -62,10 +71,13 @@ public final class UserVmJoinVOToVmConverter {
dst.description = src.getDisplayName();
dst.href = basePath + VmsRouteHandler.BASE_ROUTE + "/" + src.getUuid();
dst.status = mapStatus(src.getState());
final Date lastUpdated = src.getLastUpdated();
final Date lastUpdated = src.getLastUpdated() != null ? src.getLastUpdated() : src.getCreated();
if ("down".equals(dst.status)) {
dst.stopTime = lastUpdated.getTime();
}
if ("up".equals(dst.status)) {
dst.setStartTime(lastUpdated.getTime());
}
final Ref template = buildRef(
basePath + ApiService.BASE_ROUTE,
"templates",
@ -93,24 +105,35 @@ public final class UserVmJoinVOToVmConverter {
hostVo.getClusterUuid());
}
}
Long hostId = src.getHostId() != null ? src.getHostId() : src.getLastHostId();
if (hostId != null) {
// I want to get Host data from hostJoinDao but this is a static method without dao access.
}
dst.memory = src.getRamSize() * 1024L * 1024L;
dst.cpu = new Cpu(src.getArch(), new Topology(src.getCpu(), src.getCpu(), 1));
dst.cpu = new Cpu(src.getArch(), new Topology(src.getCpu(), 1, 1));
dst.os = new Os();
dst.os.type = src.getGuestOsId() % 2 == 0
? "windows"
: "linux";
dst.bios = new Bios();
dst.bios.type = "q35_secure_boot";
dst.type = "server";
dst.type = "desktop";
dst.origin = "ovirt";
dst.actions = null;dst.link = List.of(
if (disksResolver != null) {
List<DiskAttachment> diskAttachments = disksResolver.apply(src.getId());
dst.setDiskAttachments(new DiskAttachments(diskAttachments));
}
if (disksResolver != null) {
List<Nic> nics = nicsResolver.apply(src);
dst.setNics(new Nics(nics));
}
dst.actions = new Actions(List.of(
new Link("start", dst.href + "/start"),
new Link("stop", dst.href + "/stop"),
new Link("shutdown", dst.href + "/shutdown")
));
dst.link = List.of(
new Link("diskattachments",
dst.href + "/diskattachments"),
new Link("nics",
@ -118,16 +141,26 @@ public final class UserVmJoinVOToVmConverter {
new Link("snapshots",
dst.href + "/snapshots")
);
dst.tags = new EmptyElement();
return dst;
}
public static List<Vm> toVmList(final List<UserVmJoinVO> srcList, final Function<Long, HostJoinVO> hostResolver) {
return srcList.stream()
.map(v -> toVm(v, hostResolver))
.map(v -> toVm(v, hostResolver, null, null))
.collect(Collectors.toList());
}
public static VmAction toVmAction(final UserVmJoinVO vm) {
VmAction action = new VmAction();
final String basePath = VeeamControlService.ContextPath.value();
action.setVm(toVm(vm, null, null, null));
action.setJob(Ref.of(basePath + JobsRouteHandler.BASE_ROUTE + vm.getUuid(), vm.getUuid()));
action.setStatus("complete");
return action;
}
private static String mapStatus(final VirtualMachine.State state) {
if (state == null) {
return null;

View File

@ -24,6 +24,7 @@ import java.util.stream.Collectors;
import org.apache.cloudstack.veeam.VeeamControlService;
import org.apache.cloudstack.veeam.api.ApiService;
import org.apache.cloudstack.veeam.api.DisksRouteHandler;
import org.apache.cloudstack.veeam.api.VmsRouteHandler;
import org.apache.cloudstack.veeam.api.dto.Actions;
import org.apache.cloudstack.veeam.api.dto.Disk;
import org.apache.cloudstack.veeam.api.dto.DiskAttachment;
@ -132,24 +133,21 @@ public class VolumeJoinVOToDiskConverter {
public static DiskAttachment toDiskAttachment(final VolumeJoinVO vol) {
final DiskAttachment da = new DiskAttachment();
final String apiBase = VeeamControlService.ContextPath.value() + ApiService.BASE_ROUTE;
final String basePath = VeeamControlService.ContextPath.value();
final String apiBase = basePath + ApiService.BASE_ROUTE;
final String diskAttachmentId = vol.getUuid();
final String diskAttachmentHref = apiBase + "/diskattachments/" + diskAttachmentId;
da.id = diskAttachmentId;
da.href = diskAttachmentHref;
// Links
da.disk = Ref.of(
apiBase + "/disks/" + vol.getUuid(),
vol.getUuid()
);
da.vm = Ref.of(
apiBase + "/vms/" + vol.getVmUuid(),
basePath + VmsRouteHandler.BASE_ROUTE + "/" + vol.getVmUuid(),
vol.getVmUuid()
);
da.id = diskAttachmentId;
da.href = da.vm.href + "/diskattachements/" + diskAttachmentId;;
// Links
da.disk = toDisk(vol);
// Properties
da.active = "true";
da.bootable = "false";

View File

@ -43,7 +43,7 @@ public final class DiskAttachment {
@JsonProperty("uses_scsi_reservation")
public String usesScsiReservation;
public Ref disk;
public Disk disk;
public Ref vm;
public String href;

View File

@ -0,0 +1,61 @@
// 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.veeam.api.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Ip {
private String address;
private String gateway;
private String netmask;
private String version;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getGateway() {
return gateway;
}
public void setGateway(String gateway) {
this.gateway = gateway;
}
public String getNetmask() {
return netmask;
}
public void setNetmask(String netmask) {
this.netmask = netmask;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}

View File

@ -0,0 +1,42 @@
// 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.veeam.api.dto;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Ips {
@JacksonXmlElementWrapper(useWrapping = false)
private List<Ip> ip;
public Ips(final List<Ip> ip) {
this.ip = ip;
}
public List<Ip> getIp() {
return ip;
}
public void setIp(List<Ip> ip) {
this.ip = ip;
}
}

View File

@ -0,0 +1,75 @@
// 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.veeam.api.dto;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Job {
private String autoCleared;
private String external;
private Long lastUpdated;
private Long startTime;
private Long endTime;
private String status;
private Ref owner;
private Actions actions;
private String description;
private List<Link> link;
private String href;
private String id;
// getters and setters
public String getAutoCleared() { return autoCleared; }
public void setAutoCleared(String autoCleared) { this.autoCleared = autoCleared; }
public String getExternal() { return external; }
public void setExternal(String external) { this.external = external; }
public Long getLastUpdated() { return lastUpdated; }
public void setLastUpdated(Long lastUpdated) { this.lastUpdated = lastUpdated; }
public Long getStartTime() { return startTime; }
public void setStartTime(Long startTime) { this.startTime = startTime; }
public Long getEndTime() { return endTime; }
public void setEndTime(Long endTime) { this.endTime = endTime; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public Ref getOwner() { return owner; }
public void setOwner(Ref owner) { this.owner = owner; }
public Actions getActions() { return actions; }
public void setActions(Actions actions) { this.actions = actions; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public List<Link> getLink() { return link; }
public void setLink(List<Link> link) { this.link = link; }
public String getHref() { return href; }
public void setHref(String href) { this.href = href; }
public String getId() { return id; }
public void setId(String id) { this.id = id; }
}

View File

@ -0,0 +1,42 @@
// 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 ownershjob. 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.veeam.api.dto;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Jobs {
@JacksonXmlElementWrapper(useWrapping = false)
private List<Job> job;
public Jobs(final List<Job> job) {
this.job = job;
}
public List<Job> getJob() {
return job;
}
public void setJob(List<Job> job) {
this.job = job;
}
}

View File

@ -15,20 +15,20 @@
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.veeam.api.response;
package org.apache.cloudstack.veeam.api.dto;
import org.apache.cloudstack.veeam.api.dto.Vm;
import com.fasterxml.jackson.annotation.JsonInclude;
/**
* Required entity response:
* { "vm": { .. } }
*/
public final class VmEntityResponse {
public Vm vm;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Mac {
public VmEntityResponse() {}
private String address;
public VmEntityResponse(final Vm vm) {
this.vm = vm;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}

View File

@ -0,0 +1,131 @@
// 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.veeam.api.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Nic {
private String href;
private String id;
private String name;
private String description;
@JacksonXmlProperty(localName = "interface")
@JsonProperty("interface")
private String interfaceType;
private String linked;
private Mac mac;
private String plugged;
private Ref vnicProfile;
private Ref vm;
private ReportedDevices reportedDevices;
public Nic() {
}
public String getHref() {
return href;
}
public void setHref(final String href) {
this.href = href;
}
public String getId() {
return id;
}
public void setId(final String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(final String description) {
this.description = description;
}
public String getInterfaceType() {
return interfaceType;
}
public void setInterfaceType(String interfaceType) {
this.interfaceType = interfaceType;
}
public boolean isLinked() {
return Boolean.parseBoolean(linked);
}
public void setLinked(boolean linked) {
this.linked = Boolean.toString(linked);
}
public Mac getMac() {
return mac;
}
public void setMac(Mac mac) {
this.mac = mac;
}
public boolean isPlugged() {
return Boolean.parseBoolean(plugged);
}
public void setPlugged(boolean plugged) {
this.plugged = Boolean.toString(plugged);
}
public Ref getVnicProfile() {
return vnicProfile;
}
public void setVnicProfile(Ref vnicProfile) {
this.vnicProfile = vnicProfile;
}
public Ref getVm() {
return vm;
}
public void setVm(Ref vm) {
this.vm = vm;
}
public ReportedDevices getReportedDevices() {
return reportedDevices;
}
public void setReportedDevices(ReportedDevices reportedDevices) {
this.reportedDevices = reportedDevices;
}
}

View File

@ -0,0 +1,40 @@
// 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.veeam.api.dto;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JacksonXmlRootElement(localName = "nics")
public final class Nics {
@JsonProperty("nic")
@JacksonXmlElementWrapper(useWrapping = false)
public List<Nic> nic;
public Nics() {}
public Nics(final List<Nic> nic) {
this.nic = nic;
}
}

View File

@ -0,0 +1,93 @@
// 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.veeam.api.dto;
public class ReportedDevice {
private String comment;
private String description;
private Ips ips;
private String id;
private Mac Mac;
private String name;
private String type;
private Ref vm;
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Ips getIps() {
return ips;
}
public void setIps(Ips ips) {
this.ips = ips;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Mac getMac() {
return Mac;
}
public void setMac(Mac mac) {
Mac = mac;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Ref getVm() {
return vm;
}
public void setVm(Ref vm) {
this.vm = vm;
}
}

View File

@ -0,0 +1,42 @@
// 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.veeam.api.dto;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ReportedDevices {
@JacksonXmlElementWrapper(useWrapping = false)
private List<ReportedDevice> reportedDevice;
public ReportedDevices(final List<ReportedDevice> reportedDevice) {
this.reportedDevice = reportedDevice;
}
public List<ReportedDevice> getReportedDevice() {
return reportedDevice;
}
public void setReportedDevice(List<ReportedDevice> reportedDevice) {
this.reportedDevice = reportedDevice;
}
}

View File

@ -46,6 +46,7 @@ public final class Vm {
@JsonProperty("stop_time")
@JacksonXmlProperty(localName = "stop_time")
public Long stopTime; // epoch millis
private Long startTime; // epoch millis
public Ref template;
@ -68,6 +69,43 @@ public final class Vm {
public Actions actions; // actions.link[]
@JacksonXmlElementWrapper(useWrapping = false)
public List<Link> link; // related resources
public EmptyElement tags; // empty <tags/>
private DiskAttachments diskAttachments;
private Nics nics;
private VmInitialization initialization;
public Vm() {}
public Long getStartTime() {
return startTime;
}
public void setStartTime(Long startTime) {
this.startTime = startTime;
}
public DiskAttachments getDiskAttachments() {
return diskAttachments;
}
public void setDiskAttachments(DiskAttachments diskAttachments) {
this.diskAttachments = diskAttachments;
}
public Nics getNics() {
return nics;
}
public void setNics(Nics nics) {
this.nics = nics;
}
public VmInitialization getInitialization() {
return initialization;
}
public void setInitialization(VmInitialization initialization) {
this.initialization = initialization;
}
}

View File

@ -0,0 +1,51 @@
// 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.veeam.api.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class VmAction {
private Ref job;
private Vm vm;
private String status;
public Ref getJob() {
return job;
}
public void setJob(Ref job) {
this.job = job;
}
public Vm getVm() {
return vm;
}
public void setVm(Vm vm) {
this.vm = vm;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

View File

@ -0,0 +1,34 @@
// 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.veeam.api.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class VmInitialization {
private String contentData;
public String getContentData() {
return contentData;
}
public void setContentData(String contentData) {
this.contentData = contentData;
}
}

View File

@ -15,12 +15,10 @@
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.veeam.api.response;
package org.apache.cloudstack.veeam.api.dto;
import java.util.List;
import org.apache.cloudstack.veeam.api.dto.Vm;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@ -34,14 +32,14 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "vm" })
@JacksonXmlRootElement(localName = "vms")
public final class VmCollectionResponse {
public final class Vms {
@JsonProperty("vm")
@JacksonXmlElementWrapper(useWrapping = false)
public List<Vm> vm;
public VmCollectionResponse() {}
public Vms() {}
public VmCollectionResponse(final List<Vm> vm) {
public Vms(final List<Vm> vm) {
this.vm = vm;
}
}

View File

@ -19,6 +19,7 @@ package org.apache.cloudstack.veeam.utils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
@ -38,6 +39,7 @@ public class Mapper {
private static void configure(final ObjectMapper mapper) {
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// If you ever add enums etc:
// mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
// mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);

View File

@ -42,11 +42,12 @@
<bean id="vmsRouteHandler" class="org.apache.cloudstack.veeam.api.VmsRouteHandler"/>
<bean id="disksRouteHandler" class="org.apache.cloudstack.veeam.api.DisksRouteHandler"/>
<bean id="imageTransfersRouteHandler" class="org.apache.cloudstack.veeam.api.ImageTransfersRouteHandler"/>
<bean id="jobsRouteHandler" class="org.apache.cloudstack.veeam.api.JobsRouteHandler"/>
<bean id="veeamControlService" class="org.apache.cloudstack.veeam.VeeamControlServiceImpl" >
<property name="routeHandlers" value="#{routeHandlerRegistry.registered}" />
</bean>
<bean id="userResourceAdapter" class="org.apache.cloudstack.veeam.adapter.UserResourceAdapter"/>
<bean id="serverAdapter" class="org.apache.cloudstack.veeam.adapter.ServerAdapter"/>
</beans>

View File

@ -204,5 +204,5 @@ public interface UserVmManager extends UserVmService {
*/
boolean isVMPartOfAnyCKSCluster(VMInstanceVO vm);
boolean isDummyTemplate(HypervisorType hypervisorType, Long templateId);
boolean isBlankInstanceTemplate(VirtualMachineTemplate template);
}

View File

@ -423,7 +423,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
private static final long GiB_TO_BYTES = 1024 * 1024 * 1024;
public static final String KVM_VM_DUMMY_TEMPLATE_NAME = "kvm-vm-dummy-template";
private static final String KVM_VM_DUMMY_TEMPLATE_NAME = "kvm-vm-dummy-template";
@Inject
@ -658,7 +658,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
private boolean _instanceNameFlag;
private int _scaleRetry;
private Map<Long, VmAndCountDetails> vmIdCountMap = new ConcurrentHashMap<>();
private static VMTemplateVO KVM_VM_DUMMY_TEMPLATE;
protected static long ROOT_DEVICE_ID = 0;
@ -2505,17 +2504,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
_scaleRetry = NumbersUtil.parseInt(configs.get(Config.ScaleRetry.key()), 2);
_vmIpFetchThreadExecutor = Executors.newFixedThreadPool(VmIpFetchThreadPoolMax.value(), new NamedThreadFactory("vmIpFetchThread"));
KVM_VM_DUMMY_TEMPLATE = _templateDao.findByAccountAndName(Account.ACCOUNT_ID_SYSTEM, KVM_VM_DUMMY_TEMPLATE_NAME);
if (KVM_VM_DUMMY_TEMPLATE == null) {
KVM_VM_DUMMY_TEMPLATE = VMTemplateVO.createSystemIso(_templateDao.getNextInSequence(Long.class, "id"), KVM_VM_DUMMY_TEMPLATE_NAME, KVM_VM_DUMMY_TEMPLATE_NAME, true,
"", true, 64, Account.ACCOUNT_ID_SYSTEM, "",
"Dummy Template for KVM VM", false, 1);
KVM_VM_DUMMY_TEMPLATE.setState(VirtualMachineTemplate.State.Active);
KVM_VM_DUMMY_TEMPLATE.setFormat(ImageFormat.QCOW2);
KVM_VM_DUMMY_TEMPLATE = _templateDao.persist(KVM_VM_DUMMY_TEMPLATE);
}
logger.info("User VM Manager is configured.");
return true;
@ -3945,7 +3933,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
_accountMgr.checkAccess(owner, _diskOfferingDao.findById(diskOfferingId), zone);
// If no network is specified, find system security group enabled network
if (isDummyTemplate(hypervisor, template.getId())) {
if (isBlankInstanceTemplate(template)) {
logger.debug("Template is a dummy template for hypervisor {}, skipping network allocation in an advanced security group enabled zone", hypervisor);
} else if (networkIdList == null || networkIdList.isEmpty()) {
Network networkWithSecurityGroup = _networkModel.getNetworkWithSGWithFreeIPs(owner, zone.getId());
@ -4060,7 +4048,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
_accountMgr.checkAccess(owner, diskOffering, zone);
List<HypervisorType> vpcSupportedHTypes = _vpcMgr.getSupportedVpcHypervisors();
if (isDummyTemplate(hypervisor, template.getId())) {
if (isBlankInstanceTemplate(template)) {
logger.debug("Template is a dummy template for hypervisor {}, skipping network allocation in an advanced zone", hypervisor);
} else if (networkIdList == null || networkIdList.isEmpty()) {
NetworkVO defaultNetwork = getDefaultNetwork(zone, owner, false);
@ -4497,7 +4485,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
}
}
if (TemplateType.SYSTEM.equals(template.getTemplateType()) && !CKS_NODE.equals(vmType) && !SHAREDFSVM.equals(vmType) && !isDummyTemplate(hypervisorType, template.getId())) {
if (TemplateType.SYSTEM.equals(template.getTemplateType()) && !CKS_NODE.equals(vmType) && !SHAREDFSVM.equals(vmType) && !isBlankInstanceTemplate(template)) {
throw new InvalidParameterValueException(String.format("Unable to use system template %s to deploy a user vm", template));
}
@ -4510,7 +4498,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (CollectionUtils.isEmpty(snapshotsOnZone)) {
throw new InvalidParameterValueException("The snapshot does not exist on zone " + zone.getId());
}
} else if (!isDummyTemplate(hypervisorType, template.getId())) {
} else if (!isBlankInstanceTemplate(template)) {
List<VMTemplateZoneVO> listZoneTemplate = _templateZoneDao.listByZoneTemplate(zone.getId(), template.getId());
if (listZoneTemplate == null || listZoneTemplate.isEmpty()) {
throw new InvalidParameterValueException("The template " + template.getId() + " is not available for use");
@ -4625,7 +4613,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// by Agent Manager in order to configure default
// gateway for the vm
if (defaultNetworkNumber == 0) {
if (isDummyTemplate(hypervisorType, template.getId())) {
if (isBlankInstanceTemplate(template)) {
logger.debug("Template is a dummy template for hypervisor {}, vm can be created without a default network", hypervisorType);
} else {
throw new InvalidParameterValueException("At least 1 default network has to be specified for the vm");
@ -4791,7 +4779,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
return rootDiskSize;
} else {
// For baremetal, size can be 0 (zero)
Long templateSize = _templateDao.findById(template.getId()).getSize();
Long templateSize = template.getSize();
if (templateSize != null) {
return templateSize;
}
@ -5347,7 +5335,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
@ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", async = true)
public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException {
long vmId = cmd.getEntityId();
if (!cmd.getStartVm() || cmd.getDummy()) {
if (!cmd.getStartVm() || cmd.isBlankInstance()) {
return getUserVm(vmId);
}
Long podId = null;
@ -6495,10 +6483,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
(!(HypervisorType.KVM.equals(template.getHypervisorType()) || HypervisorType.KVM.equals(cmd.getHypervisor())))) {
throw new InvalidParameterValueException("Deploying a virtual machine with existing volume/snapshot is supported only from KVM hypervisors");
}
if (template == null && HypervisorType.KVM.equals(cmd.getHypervisor()) && cmd.getDummy()) {
template = KVM_VM_DUMMY_TEMPLATE;
if (template == null && HypervisorType.KVM.equals(cmd.getHypervisor()) && cmd.isBlankInstance()) {
template = getBlankInstanceTemplate();
logger.info("Creating launch permission for Dummy template");
LaunchPermissionVO launchPermission = new LaunchPermissionVO(KVM_VM_DUMMY_TEMPLATE.getId(), owner.getId());
LaunchPermissionVO launchPermission = new LaunchPermissionVO(template.getId(), owner.getId());
launchPermissionDao.persist(launchPermission);
}
// Make sure a valid template ID was specified
@ -6660,9 +6648,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
applyLeaseOnCreateInstance(vm, cmd.getLeaseDuration(), cmd.getLeaseExpiryAction(), svcOffering);
}
if (KVM_VM_DUMMY_TEMPLATE != null && template.getId() == KVM_VM_DUMMY_TEMPLATE.getId() && cmd instanceof DeployVMCmd && ((DeployVMCmd) cmd).getDummy()) {
if (isBlankInstanceTemplate(template) && cmd instanceof DeployVMCmd && ((DeployVMCmd) cmd).isBlankInstance()) {
logger.info("Revoking launch permission for Dummy template");
launchPermissionDao.removePermissions(KVM_VM_DUMMY_TEMPLATE.getId(), Collections.singletonList(owner.getId()));
launchPermissionDao.removePermissions(template.getId(), Collections.singletonList(owner.getId()));
}
return vm;
@ -10101,10 +10089,23 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
}
@Override
public boolean isDummyTemplate(HypervisorType hypervisorType, Long templateId) {
if (HypervisorType.KVM.equals(hypervisorType) && KVM_VM_DUMMY_TEMPLATE != null && KVM_VM_DUMMY_TEMPLATE.getId() == templateId) {
return true;
public boolean isBlankInstanceTemplate(VirtualMachineTemplate template) {
return KVM_VM_DUMMY_TEMPLATE_NAME.equals(template.getUniqueName());
}
VMTemplateVO getBlankInstanceTemplate() {
VMTemplateVO template = _templateDao.findByName(KVM_VM_DUMMY_TEMPLATE_NAME);
if (template != null) {
return template;
}
return false;
template = VMTemplateVO.createSystemIso(_templateDao.getNextInSequence(Long.class, "id"),
KVM_VM_DUMMY_TEMPLATE_NAME, KVM_VM_DUMMY_TEMPLATE_NAME, true,
"", true, 64, Account.ACCOUNT_ID_SYSTEM, "",
"Dummy Template for KVM VM", false, 1);
template.setState(VirtualMachineTemplate.State.Active);
template.setFormat(ImageFormat.QCOW2);
template = _templateDao.persist(template);
// _templateDao.remove(template.getId());
return template;
}
}

View File

@ -478,6 +478,16 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
return imageTransferDao.findById(imageTransfer.getId());
}
@Override
public boolean cancelImageTransfer(long imageTransferId) {
ImageTransferVO imageTransfer = imageTransferDao.findById(imageTransferId);
if (imageTransfer == null) {
throw new CloudRuntimeException("Image transfer not found: " + imageTransferId);
}
// ToDo: Implement cancel logic
return true;
}
private void finalizeDownloadImageTransfer(ImageTransferVO imageTransfer) {
String transferId = imageTransfer.getUuid();
@ -552,8 +562,11 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
@Override
public boolean finalizeImageTransfer(FinalizeImageTransferCmd cmd) {
Long imageTransferId = cmd.getImageTransferId();
return finalizeImageTransfer(cmd.getImageTransferId());
}
@Override
public boolean finalizeImageTransfer(final long imageTransferId) {
ImageTransferVO imageTransfer = imageTransferDao.findById(imageTransferId);
if (imageTransfer == null) {
throw new CloudRuntimeException("Image transfer not found: " + imageTransferId);
@ -566,6 +579,8 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
}
imageTransfer.setPhase(ImageTransferVO.Phase.finished);
imageTransferDao.update(imageTransfer.getId(), imageTransfer);
// ToDo: check this
// imageTransferDao.remove(imageTransfer.getId());
return true;
}
@ -656,7 +671,8 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
response.setBackupId(backup.getUuid());
}
Long volumeId = imageTransferVO.getDiskId();
Volume volume = volumeDao.findById(volumeId);
// ToDo: fix volume deletion leaving orphan image transfer record
Volume volume = volumeDao.findByIdIncludingRemoved(volumeId);
response.setDiskId(volume.getUuid());
response.setTransferUrl(imageTransferVO.getTransferUrl());
response.setPhase(imageTransferVO.getPhase().toString());