mirror of https://github.com/apache/cloudstack.git
wip: changes for imagetransfer handling
Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
parent
10f65b67d7
commit
7b45d2e118
|
|
@ -22,6 +22,7 @@ import java.net.MalformedURLException;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.offering.DiskOffering;
|
||||
import com.cloud.user.Account;
|
||||
|
|
@ -70,6 +71,10 @@ public interface VolumeApiService {
|
|||
*/
|
||||
Volume allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationException;
|
||||
|
||||
Volume allocVolume(long ownerId, Long zoneId, Long diskOfferingId, Long vmId, Long snapshotId, String name,
|
||||
Long cmdSize, Boolean displayVolume, Long cmdMinIops, Long cmdMaxIops, String customId)
|
||||
throws ResourceAllocationException;
|
||||
|
||||
/**
|
||||
* Creates the volume based on the given criteria
|
||||
*
|
||||
|
|
@ -80,6 +85,8 @@ public interface VolumeApiService {
|
|||
*/
|
||||
Volume createVolume(CreateVolumeCmd cmd);
|
||||
|
||||
Volume createVolume(long volumeId, Long vmId, Long snapshotId, Long storageId, Boolean display);
|
||||
|
||||
/**
|
||||
* Resizes the volume based on the given criteria
|
||||
*
|
||||
|
|
@ -203,4 +210,6 @@ public interface VolumeApiService {
|
|||
Pair<String, String> checkAndRepairVolume(CheckAndRepairVolumeCmd cmd) throws ResourceAllocationException;
|
||||
|
||||
Long getVolumePhysicalSize(Storage.ImageFormat format, String path, String chainInfo);
|
||||
|
||||
Long getCustomDiskOfferingIdForVolumeUpload(Account owner, DataCenter zone);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ public interface IncrementalBackupService extends PluggableService {
|
|||
*/
|
||||
ImageTransferResponse createImageTransfer(CreateImageTransferCmd cmd);
|
||||
|
||||
ImageTransfer createImageTransfer(long volumeId, Long backupId, ImageTransfer.Direction direction);
|
||||
|
||||
/**
|
||||
* Finalize an image transfer
|
||||
* Marks transfer as complete (NBD is closed globally in finalize backup)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.apache.cloudstack.veeam.utils.ResponseMapper;
|
||||
import org.apache.cloudstack.veeam.utils.Mapper;
|
||||
import org.apache.cloudstack.veeam.utils.ResponseWriter;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
|
@ -38,11 +38,12 @@ public class VeeamControlServlet extends HttpServlet {
|
|||
private static final Logger LOGGER = LogManager.getLogger(VeeamControlServlet.class);
|
||||
|
||||
private final ResponseWriter writer;
|
||||
private final Mapper mapper;
|
||||
private final List<RouteHandler> routeHandlers;
|
||||
|
||||
public VeeamControlServlet(List<RouteHandler> routeHandlers) {
|
||||
this.routeHandlers = routeHandlers;
|
||||
ResponseMapper mapper = new ResponseMapper();
|
||||
mapper = new Mapper();
|
||||
writer = new ResponseWriter(mapper);
|
||||
}
|
||||
|
||||
|
|
@ -50,6 +51,10 @@ public class VeeamControlServlet extends HttpServlet {
|
|||
return writer;
|
||||
}
|
||||
|
||||
public Mapper getMapper() {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
|
||||
String method = req.getMethod();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,330 @@
|
|||
// 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.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.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
|
||||
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");
|
||||
}
|
||||
if (StringUtils.isBlank(request.provisionedSize)) {
|
||||
throw new InvalidParameterValueException("Provisioned size must be specified");
|
||||
}
|
||||
long sizeInGb;
|
||||
try {
|
||||
sizeInGb = Long.parseLong(request.provisionedSize);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new InvalidParameterValueException("Invalid provisioned size: " + request.provisionedSize);
|
||||
}
|
||||
if (sizeInGb <= 0) {
|
||||
throw new InvalidParameterValueException("Provisioned size must be greater than zero");
|
||||
}
|
||||
sizeInGb = Math.max(1L, sizeInGb / (1024L * 1024L * 1024L));
|
||||
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, sizeInGb);
|
||||
} finally {
|
||||
CallContext.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Disk createDisk(Account serviceAccount, StoragePoolVO pool, String name, Long diskOfferingId, long sizeInGb) {
|
||||
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);
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,22 +26,23 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.apache.cloudstack.veeam.RouteHandler;
|
||||
import org.apache.cloudstack.veeam.VeeamControlServlet;
|
||||
import org.apache.cloudstack.veeam.api.converter.VolumeJoinVOToDiskConverter;
|
||||
import org.apache.cloudstack.veeam.adapter.UserResourceAdapter;
|
||||
import org.apache.cloudstack.veeam.api.dto.Disk;
|
||||
import org.apache.cloudstack.veeam.api.dto.Disks;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.apache.cloudstack.veeam.utils.PathUtil;
|
||||
|
||||
import com.cloud.api.query.dao.VolumeJoinDao;
|
||||
import com.cloud.api.query.vo.VolumeJoinVO;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
public class DisksRouteHandler extends ManagerBase implements RouteHandler {
|
||||
public static final String BASE_ROUTE = "/api/disks";
|
||||
|
||||
@Inject
|
||||
VolumeJoinDao volumeJoinDao;
|
||||
UserResourceAdapter userResourceAdapter;
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
|
|
@ -93,33 +94,32 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler {
|
|||
|
||||
public void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
|
||||
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
|
||||
final List<Disk> result = VolumeJoinVOToDiskConverter.toDiskList(listDisks());
|
||||
final List<Disk> result = userResourceAdapter.listAllDisks();
|
||||
final Disks response = new Disks(result);
|
||||
|
||||
io.getWriter().write(resp, 400, response, outFormat);
|
||||
io.getWriter().write(resp, 200, response, outFormat);
|
||||
}
|
||||
|
||||
public 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);
|
||||
|
||||
io.getWriter().write(resp, 400, "Unable to process at the moment", outFormat);
|
||||
}
|
||||
|
||||
protected List<VolumeJoinVO> listDisks() {
|
||||
return volumeJoinDao.listAll();
|
||||
try {
|
||||
Disk request = io.getMapper().jsonMapper().readValue(data, Disk.class);
|
||||
Disk response = userResourceAdapter.handleCreateDisk(request);
|
||||
io.getWriter().write(resp, 201, response, outFormat);
|
||||
} catch (JsonProcessingException | CloudRuntimeException e) {
|
||||
io.getWriter().write(resp, 400, e.getMessage(), outFormat);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
|
||||
final VeeamControlServlet io) throws IOException {
|
||||
final VolumeJoinVO volumeJoinVO = volumeJoinDao.findByUuid(id);
|
||||
if (volumeJoinVO == null) {
|
||||
io.notFound(resp, "DataCenter not found: " + id, outFormat);
|
||||
return;
|
||||
try {
|
||||
Disk response = userResourceAdapter.getDisk(id);
|
||||
io.getWriter().write(resp, 200, response, outFormat);
|
||||
} catch (InvalidParameterValueException e) {
|
||||
io.getWriter().write(resp, 404, e.getMessage(), outFormat);
|
||||
}
|
||||
Disk response = VolumeJoinVOToDiskConverter.toDisk(volumeJoinVO);
|
||||
|
||||
io.getWriter().write(resp, 200, response, outFormat);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
// 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.UserResourceAdapter;
|
||||
import org.apache.cloudstack.veeam.api.dto.ImageTransfer;
|
||||
import org.apache.cloudstack.veeam.api.dto.ImageTransfers;
|
||||
import org.apache.cloudstack.veeam.utils.Negotiation;
|
||||
import org.apache.cloudstack.veeam.utils.PathUtil;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
public class ImageTransfersRouteHandler extends ManagerBase implements RouteHandler {
|
||||
public static final String BASE_ROUTE = "/api/imagetransfers";
|
||||
|
||||
@Inject
|
||||
UserResourceAdapter userResourceAdapter;
|
||||
|
||||
@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();
|
||||
final String sanitizedPath = getSanitizedPath(path);
|
||||
if (sanitizedPath.equals(BASE_ROUTE)) {
|
||||
if ("GET".equalsIgnoreCase(method)) {
|
||||
handleGet(req, resp, outFormat, io);
|
||||
return;
|
||||
}
|
||||
if ("POST".equalsIgnoreCase(method)) {
|
||||
handlePost(req, resp, outFormat, io);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!"GET".equalsIgnoreCase(method)) {
|
||||
io.methodNotAllowed(resp, "GET", outFormat);
|
||||
return;
|
||||
}
|
||||
Pair<String, String> idAndSubPath = PathUtil.extractIdAndSubPath(sanitizedPath, BASE_ROUTE);
|
||||
if (idAndSubPath != null) {
|
||||
// /api/imagetransfers/{id}
|
||||
if (idAndSubPath.first() != null) {
|
||||
if (idAndSubPath.second() == null) {
|
||||
handleGetById(idAndSubPath.first(), resp, outFormat, io);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Not found");
|
||||
}
|
||||
|
||||
public void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
|
||||
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
|
||||
final List<ImageTransfer> result = userResourceAdapter.listAllImageTransfers();
|
||||
final ImageTransfers response = new ImageTransfers();
|
||||
response.setImageTransfer(result);
|
||||
|
||||
io.getWriter().write(resp, 400, response, outFormat);
|
||||
}
|
||||
|
||||
public 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/imagetransfers endpoint, but method: POST is not supported atm. 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);
|
||||
} catch (JsonProcessingException | CloudRuntimeException e) {
|
||||
io.getWriter().write(resp, 400, e.getMessage(), outFormat);
|
||||
}
|
||||
}
|
||||
|
||||
public 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);
|
||||
} catch (InvalidParameterValueException e) {
|
||||
io.getWriter().write(resp, 404, e.getMessage(), outFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
// 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.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.cloudstack.backup.ImageTransferVO;
|
||||
import org.apache.cloudstack.veeam.VeeamControlService;
|
||||
import org.apache.cloudstack.veeam.api.DisksRouteHandler;
|
||||
import org.apache.cloudstack.veeam.api.HostsRouteHandler;
|
||||
import org.apache.cloudstack.veeam.api.ImageTransfersRouteHandler;
|
||||
import org.apache.cloudstack.veeam.api.dto.ImageTransfer;
|
||||
import org.apache.cloudstack.veeam.api.dto.Link;
|
||||
import org.apache.cloudstack.veeam.api.dto.Ref;
|
||||
|
||||
import com.cloud.api.query.vo.HostJoinVO;
|
||||
import com.cloud.api.query.vo.VolumeJoinVO;
|
||||
|
||||
public class ImageTransferVOToImageTransferConverter {
|
||||
public static ImageTransfer toImageTransfer(ImageTransferVO vo, final Function<Long, HostJoinVO> hostResolver,
|
||||
final Function<Long, VolumeJoinVO> volumeResolver) {
|
||||
ImageTransfer imageTransfer = new ImageTransfer();
|
||||
final String basePath = VeeamControlService.ContextPath.value();
|
||||
imageTransfer.setId(vo.getUuid());
|
||||
imageTransfer.setHref(basePath + ImageTransfersRouteHandler.BASE_ROUTE + "/" + vo.getUuid());
|
||||
imageTransfer.setActive(Boolean.toString(true));
|
||||
imageTransfer.setDirection(vo.getDirection().name());
|
||||
imageTransfer.setFormat("cow");
|
||||
imageTransfer.setInactivityTimeout(Integer.toString(60));
|
||||
imageTransfer.setPhase(vo.getPhase().name());
|
||||
imageTransfer.setProxyUrl(vo.getTransferUrl());
|
||||
imageTransfer.setShallow(Boolean.toString(false));
|
||||
imageTransfer.setTimeoutPolicy("legacy");
|
||||
imageTransfer.setTransferUrl(vo.getTransferUrl());
|
||||
imageTransfer.setTransferred(Long.toString(0));
|
||||
if (hostResolver != null) {
|
||||
HostJoinVO hostVo = hostResolver.apply(vo.getHostId());
|
||||
if (hostVo != null) {
|
||||
imageTransfer.setHost(Ref.of(basePath + HostsRouteHandler.BASE_ROUTE + "/" + hostVo.getUuid(), hostVo.getUuid()));
|
||||
}
|
||||
}
|
||||
if (volumeResolver != null) {
|
||||
VolumeJoinVO volumeVo = volumeResolver.apply(vo.getDiskId());
|
||||
if (volumeVo != null) {
|
||||
imageTransfer.setDisk(Ref.of(basePath + DisksRouteHandler.BASE_ROUTE + "/" + volumeVo.getUuid(), volumeVo.getUuid()));
|
||||
}
|
||||
}
|
||||
final List<Link> links = new ArrayList<>();
|
||||
links.add(getLink(imageTransfer, "cancel"));
|
||||
links.add(getLink(imageTransfer, "resume"));
|
||||
links.add(getLink(imageTransfer, "pause"));
|
||||
links.add(getLink(imageTransfer, "finalize"));
|
||||
links.add(getLink(imageTransfer, "extend"));
|
||||
return imageTransfer;
|
||||
}
|
||||
|
||||
public static List<ImageTransfer> toImageTransferList(List<? extends ImageTransferVO> vos,
|
||||
final Function<Long, HostJoinVO> hostResolver,
|
||||
final Function<Long, VolumeJoinVO> volumeResolver) {
|
||||
return vos.stream().map(vo -> toImageTransfer(vo, hostResolver, volumeResolver))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static Link getLink(ImageTransfer it, String rel) {
|
||||
final Link link = new Link();
|
||||
link.rel = rel;
|
||||
link.href = it.getHref() + "/" + rel;
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
|
@ -97,8 +97,8 @@ public class VolumeJoinVOToDiskConverter {
|
|||
|
||||
// Disk profile (optional)
|
||||
disk.diskProfile = Ref.of(
|
||||
basePath + "/diskprofiles/" + vol.getDiskOfferingId(),
|
||||
String.valueOf(vol.getDiskOfferingId())
|
||||
basePath + "/diskprofiles/" + vol.getDiskOfferingUuid(),
|
||||
String.valueOf(vol.getDiskOfferingUuid())
|
||||
);
|
||||
|
||||
// Storage domains
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
|||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public final class Actions {
|
||||
public List<ActionLink> link;
|
||||
public List<Link> link;
|
||||
|
||||
public Actions() {}
|
||||
|
||||
public Actions(final List<ActionLink> link) {
|
||||
public Actions(final List<Link> link) {
|
||||
this.link = link;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
// 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.JsonProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
|
||||
public class Backup {
|
||||
|
||||
@JsonProperty("creation_date")
|
||||
@JacksonXmlProperty(localName = "creation_date")
|
||||
private String creationDate;
|
||||
|
||||
public String getCreationDate() {
|
||||
return creationDate;
|
||||
}
|
||||
|
||||
public void setCreationDate(String creationDate) {
|
||||
this.creationDate = creationDate;
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +45,9 @@ public final class Disk {
|
|||
@JsonProperty("propagate_errors")
|
||||
public String propagateErrors;
|
||||
|
||||
@JsonProperty("initial_size")
|
||||
public String initialSize;
|
||||
|
||||
@JsonProperty("provisioned_size")
|
||||
public String provisionedSize;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,202 @@
|
|||
// 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 = "image_transfer")
|
||||
public class ImageTransfer {
|
||||
|
||||
private String id;
|
||||
private String href;
|
||||
|
||||
private String active;
|
||||
private String direction;
|
||||
private String format;
|
||||
|
||||
@JsonProperty("inactivity_timeout")
|
||||
private String inactivityTimeout;
|
||||
|
||||
private String phase;
|
||||
|
||||
@JsonProperty("proxy_url")
|
||||
private String proxyUrl;
|
||||
|
||||
private String shallow;
|
||||
|
||||
@JsonProperty("timeout_policy")
|
||||
private String timeoutPolicy;
|
||||
|
||||
@JsonProperty("transfer_url")
|
||||
private String transferUrl;
|
||||
|
||||
private String transferred;
|
||||
|
||||
private Backup backup;
|
||||
|
||||
private Ref host;
|
||||
private Ref image;
|
||||
private Ref disk;
|
||||
private Actions actions;
|
||||
|
||||
@JacksonXmlElementWrapper(useWrapping = false)
|
||||
public List<Link> link;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getHref() {
|
||||
return href;
|
||||
}
|
||||
|
||||
public void setHref(String href) {
|
||||
this.href = href;
|
||||
}
|
||||
|
||||
public String getActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(String active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public String getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setDirection(String direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public void setFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public String getInactivityTimeout() {
|
||||
return inactivityTimeout;
|
||||
}
|
||||
|
||||
public void setInactivityTimeout(String inactivityTimeout) {
|
||||
this.inactivityTimeout = inactivityTimeout;
|
||||
}
|
||||
|
||||
public String getPhase() {
|
||||
return phase;
|
||||
}
|
||||
|
||||
public void setPhase(String phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
public String getProxyUrl() {
|
||||
return proxyUrl;
|
||||
}
|
||||
|
||||
public void setProxyUrl(String proxyUrl) {
|
||||
this.proxyUrl = proxyUrl;
|
||||
}
|
||||
|
||||
public String getShallow() {
|
||||
return shallow;
|
||||
}
|
||||
|
||||
public void setShallow(String shallow) {
|
||||
this.shallow = shallow;
|
||||
}
|
||||
|
||||
public String getTimeoutPolicy() {
|
||||
return timeoutPolicy;
|
||||
}
|
||||
|
||||
public void setTimeoutPolicy(String timeoutPolicy) {
|
||||
this.timeoutPolicy = timeoutPolicy;
|
||||
}
|
||||
|
||||
public String getTransferUrl() {
|
||||
return transferUrl;
|
||||
}
|
||||
|
||||
public void setTransferUrl(String transferUrl) {
|
||||
this.transferUrl = transferUrl;
|
||||
}
|
||||
|
||||
public String getTransferred() {
|
||||
return transferred;
|
||||
}
|
||||
|
||||
public void setTransferred(String transferred) {
|
||||
this.transferred = transferred;
|
||||
}
|
||||
|
||||
public Backup getBackup() {
|
||||
return backup;
|
||||
}
|
||||
|
||||
public void setBackup(Backup backup) {
|
||||
this.backup = backup;
|
||||
}
|
||||
|
||||
public Ref getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(Ref host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public Ref getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public void setImage(Ref image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public Ref getDisk() {
|
||||
return disk;
|
||||
}
|
||||
|
||||
public void setDisk(Ref disk) {
|
||||
this.disk = disk;
|
||||
}
|
||||
|
||||
public Actions getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
public void setActions(Actions actions) {
|
||||
this.actions = actions;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,23 +17,23 @@
|
|||
|
||||
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.JacksonXmlRootElement;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public final class ActionLink {
|
||||
public String rel; // start/stop/reboot/shutdown...
|
||||
public String href; // /api/vms/{id}/start
|
||||
public String method; // "post"
|
||||
@JacksonXmlRootElement(localName = "image_transfers")
|
||||
public class ImageTransfers {
|
||||
@JsonProperty("image_transfer")
|
||||
private List<ImageTransfer> imageTransfer;
|
||||
|
||||
public ActionLink() {}
|
||||
|
||||
public ActionLink(final String rel, final String href, final String method) {
|
||||
this.rel = rel;
|
||||
this.href = href;
|
||||
this.method = method;
|
||||
public List<ImageTransfer> getImageTransfer() {
|
||||
return imageTransfer;
|
||||
}
|
||||
|
||||
public static ActionLink post(final String rel, final String href) {
|
||||
return new ActionLink(rel, href, "post");
|
||||
public void setImageTransfer(List<ImageTransfer> imageTransfer) {
|
||||
this.imageTransfer = imageTransfer;
|
||||
}
|
||||
}
|
||||
|
|
@ -23,11 +23,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
|
||||
public class ResponseMapper {
|
||||
public class Mapper {
|
||||
private final ObjectMapper json;
|
||||
private final XmlMapper xml;
|
||||
|
||||
public ResponseMapper() {
|
||||
public Mapper() {
|
||||
this.json = new ObjectMapper();
|
||||
this.xml = new XmlMapper();
|
||||
|
||||
|
|
@ -30,9 +30,9 @@ import org.apache.logging.log4j.Logger;
|
|||
public final class ResponseWriter {
|
||||
private static final Logger LOGGER = LogManager.getLogger(ResponseWriter.class);
|
||||
|
||||
private final ResponseMapper mapper;
|
||||
private final Mapper mapper;
|
||||
|
||||
public ResponseWriter(final ResponseMapper mapper) {
|
||||
public ResponseWriter(final Mapper mapper) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
<property name="typeClass" value="org.apache.cloudstack.veeam.RouteHandler" />
|
||||
</bean>
|
||||
|
||||
<bean id="veeamControlSsoService" class="org.apache.cloudstack.veeam.sso.SsoService"/>
|
||||
<bean id="veeamControlApiService" class="org.apache.cloudstack.veeam.api.ApiService" />
|
||||
<bean id="dataCentersRouteHandler" class="org.apache.cloudstack.veeam.api.DataCentersRouteHandler"/>
|
||||
<bean id="clustersRouteHandler" class="org.apache.cloudstack.veeam.api.ClustersRouteHandler"/>
|
||||
|
|
@ -39,9 +40,12 @@
|
|||
<bean id="vnicProfilesRouteHandler" class="org.apache.cloudstack.veeam.api.VnicProfilesRouteHandler"/>
|
||||
<bean id="vmsRouteHandler" class="org.apache.cloudstack.veeam.api.VmsRouteHandler"/>
|
||||
<bean id="disksRouteHandler" class="org.apache.cloudstack.veeam.api.DisksRouteHandler"/>
|
||||
<bean id="veeamControlSsoService" class="org.apache.cloudstack.veeam.sso.SsoService"/>
|
||||
<bean id="imageTransfersRouteHandler" class="org.apache.cloudstack.veeam.api.ImageTransfersRouteHandler"/>
|
||||
|
||||
<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"/>
|
||||
|
||||
</beans>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package org.apache.cloudstack.veeam;
|
||||
|
||||
import org.apache.cloudstack.veeam.api.dto.ImageTransfer;
|
||||
import org.apache.cloudstack.veeam.utils.Mapper;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class VeeamControlServiceImplTest {
|
||||
|
||||
@Test
|
||||
public void test_parseImageTransfer() {
|
||||
String data = "{\"active\":false,\"direction\":\"upload\",\"format\":\"cow\",\"inactivity_timeout\":3600,\"phase\":\"cancelled\",\"shallow\":false,\"transferred\":0,\"link\":[],\"disk\":{\"id\":\"dba4d72d-01de-4267-aa8e-305996b53599\"},\"image\":{},\"backup\":{\"creation_date\":0}}";
|
||||
Mapper mapper = new Mapper();
|
||||
try {
|
||||
ImageTransfer request = mapper.jsonMapper().readValue(data, ImageTransfer.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
|||
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||
|
||||
import com.cloud.api.query.vo.VolumeJoinVO;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
|
|
@ -36,4 +37,6 @@ public interface VolumeJoinDao extends GenericDao<VolumeJoinVO, Long> {
|
|||
List<VolumeJoinVO> searchByIds(Long... ids);
|
||||
|
||||
List<VolumeJoinVO> listByInstanceId(long instanceId);
|
||||
|
||||
List<VolumeJoinVO> listByHypervisor(Hypervisor.HypervisorType hypervisorType);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import com.cloud.user.VmDiskStatisticsVO;
|
|||
import com.cloud.user.dao.VmDiskStatisticsDao;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
|
||||
@Component
|
||||
public class VolumeJoinDaoImpl extends GenericDaoBaseWithTagInformation<VolumeJoinVO, VolumeResponse> implements VolumeJoinDao {
|
||||
|
|
@ -379,4 +380,16 @@ public class VolumeJoinDaoImpl extends GenericDaoBaseWithTagInformation<VolumeJo
|
|||
return search(sc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VolumeJoinVO> listByHypervisor(Hypervisor.HypervisorType hypervisorType) {
|
||||
SearchBuilder<VolumeJoinVO> sb = createSearchBuilder();
|
||||
sb.and("vmType", sb.entity().getVmType(), SearchCriteria.Op.EQ);
|
||||
sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ);
|
||||
sb.done();
|
||||
SearchCriteria<VolumeJoinVO> sc = sb.create();
|
||||
sc.setParameters("vmType", VirtualMachine.Type.User);
|
||||
sc.setParameters("hypervisorType", hypervisorType);
|
||||
return search(sc, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -639,21 +639,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
return null;
|
||||
}
|
||||
|
||||
private Long getCustomDiskOfferingIdForVolumeUpload(Account owner, DataCenter zone) {
|
||||
Long offeringId = getDefaultCustomOfferingId(owner, zone);
|
||||
if (offeringId != null) {
|
||||
return offeringId;
|
||||
}
|
||||
List<DiskOfferingVO> offerings = _diskOfferingDao.findCustomDiskOfferings();
|
||||
for (DiskOfferingVO offering : offerings) {
|
||||
try {
|
||||
_configMgr.checkDiskOfferingAccess(owner, offering, zone);
|
||||
return offering.getId();
|
||||
} catch (PermissionDeniedException ignored) {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@DB
|
||||
protected VolumeVO persistVolume(final Account owner, final Long zoneId, final String volumeName, final String url, final String format, final Long diskOfferingId, final Volume.State state) {
|
||||
return Transaction.execute(new TransactionCallbackWithException<VolumeVO, CloudRuntimeException>() {
|
||||
|
|
@ -719,17 +704,31 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
* If the retrieved volume name is null, empty or blank, then A random name
|
||||
* will be generated using getRandomVolumeName method.
|
||||
*
|
||||
* @param cmd
|
||||
* @param userSpecifiedName
|
||||
* @return Either the retrieved name or a random name.
|
||||
*/
|
||||
public String getVolumeNameFromCommand(CreateVolumeCmd cmd) {
|
||||
String userSpecifiedName = cmd.getVolumeName();
|
||||
|
||||
if (StringUtils.isBlank(userSpecifiedName)) {
|
||||
userSpecifiedName = getRandomVolumeName();
|
||||
public String getVolumeNameFromCommand(String userSpecifiedName) {
|
||||
if (StringUtils.isNotBlank(userSpecifiedName)) {
|
||||
return userSpecifiedName;
|
||||
}
|
||||
|
||||
return userSpecifiedName;
|
||||
return getRandomVolumeName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getCustomDiskOfferingIdForVolumeUpload(Account owner, DataCenter zone) {
|
||||
Long offeringId = getDefaultCustomOfferingId(owner, zone);
|
||||
if (offeringId != null) {
|
||||
return offeringId;
|
||||
}
|
||||
List<DiskOfferingVO> offerings = _diskOfferingDao.findCustomDiskOfferings();
|
||||
for (DiskOfferingVO offering : offerings) {
|
||||
try {
|
||||
_configMgr.checkDiskOfferingAccess(owner, offering, zone);
|
||||
return offering.getId();
|
||||
} catch (PermissionDeniedException ignored) {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -741,11 +740,20 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
@DB
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", create = true)
|
||||
public VolumeVO allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationException {
|
||||
return allocVolume(cmd.getEntityOwnerId(), cmd.getZoneId(), cmd.getDiskOfferingId(), cmd.getVirtualMachineId(),
|
||||
cmd.getSnapshotId(), getVolumeNameFromCommand(cmd.getVolumeName()), cmd.getSize(),
|
||||
cmd.getDisplayVolume(), cmd.getMinIops(), cmd.getMaxIops(), cmd.getCustomId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", create = true)
|
||||
public VolumeVO allocVolume(long ownerId, Long zoneId, Long diskOfferingId, Long vmId, Long snapshotId,
|
||||
String name, Long cmdSize, Boolean displayVolume, Long cmdMinIops, Long cmdMaxIops, String customId)
|
||||
throws ResourceAllocationException {
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
|
||||
long ownerId = cmd.getEntityOwnerId();
|
||||
Account owner = _accountMgr.getActiveAccountById(ownerId);
|
||||
Boolean displayVolume = cmd.getDisplayVolume();
|
||||
|
||||
// permission check
|
||||
_accountMgr.checkAccess(caller, null, true, _accountMgr.getActiveAccountById(ownerId));
|
||||
|
|
@ -758,8 +766,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
}
|
||||
}
|
||||
|
||||
Long zoneId = cmd.getZoneId();
|
||||
Long diskOfferingId = null;
|
||||
DiskOfferingVO diskOffering = null;
|
||||
Long size = null;
|
||||
Long minIops = null;
|
||||
|
|
@ -768,13 +774,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
VolumeVO parentVolume = null;
|
||||
|
||||
// validate input parameters before creating the volume
|
||||
if (cmd.getSnapshotId() == null && cmd.getDiskOfferingId() == null) {
|
||||
if (snapshotId == null && diskOfferingId == null) {
|
||||
throw new InvalidParameterValueException("At least one of disk Offering ID or snapshot ID must be passed whilst creating volume");
|
||||
}
|
||||
|
||||
// disallow passing disk offering ID with DATA disk volume snapshots
|
||||
if (cmd.getSnapshotId() != null && cmd.getDiskOfferingId() != null) {
|
||||
SnapshotVO snapshot = _snapshotDao.findById(cmd.getSnapshotId());
|
||||
if (snapshotId != null && diskOfferingId != null) {
|
||||
SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
|
||||
if (snapshot != null) {
|
||||
parentVolume = _volsDao.findByIdIncludingRemoved(snapshot.getVolumeId());
|
||||
if (parentVolume != null && parentVolume.getVolumeType() != Volume.Type.ROOT)
|
||||
|
|
@ -784,10 +790,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
}
|
||||
|
||||
Map<String, String> details = new HashMap<>();
|
||||
if (cmd.getDiskOfferingId() != null) { // create a new volume
|
||||
|
||||
diskOfferingId = cmd.getDiskOfferingId();
|
||||
size = cmd.getSize();
|
||||
if (diskOfferingId != null) { // create a new volume
|
||||
size = cmdSize;
|
||||
Long sizeInGB = size;
|
||||
if (size != null) {
|
||||
if (size > 0) {
|
||||
|
|
@ -833,8 +837,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
|
||||
if (isCustomizedIops != null) {
|
||||
if (isCustomizedIops) {
|
||||
minIops = cmd.getMinIops();
|
||||
maxIops = cmd.getMaxIops();
|
||||
minIops = cmdMinIops;
|
||||
maxIops = cmdMaxIops;
|
||||
|
||||
if (minIops == null && maxIops == null) {
|
||||
minIops = 0L;
|
||||
|
|
@ -866,8 +870,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
}
|
||||
}
|
||||
|
||||
if (cmd.getSnapshotId() != null) { // create volume from snapshot
|
||||
Long snapshotId = cmd.getSnapshotId();
|
||||
if (snapshotId != null) { // create volume from snapshot
|
||||
SnapshotVO snapshotCheck = _snapshotDao.findById(snapshotId);
|
||||
if (snapshotCheck == null) {
|
||||
throw new InvalidParameterValueException("unable to find a snapshot with id " + snapshotId);
|
||||
|
|
@ -918,7 +921,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
|
||||
// one step operation - create volume in VM's cluster and attach it
|
||||
// to the VM
|
||||
Long vmId = cmd.getVirtualMachineId();
|
||||
if (vmId != null) {
|
||||
// Check that the virtual machine ID is valid and it's a user vm
|
||||
UserVmVO vm = _userVmDao.findById(vmId);
|
||||
|
|
@ -960,10 +962,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
throw new InvalidParameterValueException("Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it");
|
||||
}
|
||||
|
||||
String userSpecifiedName = getVolumeNameFromCommand(cmd);
|
||||
String userSpecifiedName = getVolumeNameFromCommand(name);
|
||||
|
||||
return commitVolume(cmd.getSnapshotId(), caller, owner, displayVolume, zoneId, diskOfferingId, provisioningType, size, minIops, maxIops, parentVolume, userSpecifiedName,
|
||||
_uuidMgr.generateUuid(Volume.class, cmd.getCustomId()), details);
|
||||
return commitVolume(snapshotId, caller, owner, displayVolume, zoneId, diskOfferingId, provisioningType, size, minIops, maxIops, parentVolume, userSpecifiedName,
|
||||
_uuidMgr.generateUuid(Volume.class, customId), details);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1075,25 +1077,33 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
@DB
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", async = true)
|
||||
public VolumeVO createVolume(CreateVolumeCmd cmd) {
|
||||
VolumeVO volume = _volsDao.findById(cmd.getEntityId());
|
||||
return createVolume(cmd.getEntityId(), cmd.getVirtualMachineId(), cmd.getSnapshotId(), cmd.getStorageId(),
|
||||
cmd.getDisplayVolume());
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", async = true)
|
||||
public VolumeVO createVolume(long volumeId, Long vmId, Long snapshotId, Long storageId, Boolean display) {
|
||||
VolumeVO volume = _volsDao.findById(volumeId);
|
||||
boolean created = true;
|
||||
|
||||
try {
|
||||
if (cmd.getSnapshotId() != null) {
|
||||
volume = createVolumeFromSnapshot(volume, cmd.getSnapshotId(), cmd.getVirtualMachineId());
|
||||
if (snapshotId != null) {
|
||||
volume = createVolumeFromSnapshot(volume, snapshotId, vmId);
|
||||
if (volume.getState() != Volume.State.Ready) {
|
||||
created = false;
|
||||
}
|
||||
|
||||
// if VM Id is provided, attach the volume to the VM
|
||||
if (cmd.getVirtualMachineId() != null) {
|
||||
if (vmId != null) {
|
||||
try {
|
||||
attachVolumeToVM(cmd.getVirtualMachineId(), volume.getId(), volume.getDeviceId(), false);
|
||||
attachVolumeToVM(vmId, volume.getId(), volume.getDeviceId(), false);
|
||||
} catch (Exception ex) {
|
||||
StringBuilder message = new StringBuilder("Volume: ");
|
||||
message.append(volume.getUuid());
|
||||
message.append(" created successfully, but failed to attach the newly created volume to VM: ");
|
||||
message.append(cmd.getVirtualMachineId());
|
||||
message.append(vmId);
|
||||
message.append(" due to error: ");
|
||||
message.append(ex.getMessage());
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
|
@ -1102,20 +1112,20 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||
throw new CloudRuntimeException(message.toString());
|
||||
}
|
||||
}
|
||||
} else if (cmd.getStorageId() != null) {
|
||||
allocateVolumeOnStorage(cmd.getEntityId(), cmd.getStorageId());
|
||||
} else if (storageId != null) {
|
||||
allocateVolumeOnStorage(volumeId, storageId);
|
||||
}
|
||||
return volume;
|
||||
} catch (Exception e) {
|
||||
created = false;
|
||||
VolumeInfo vol = volFactory.getVolume(cmd.getEntityId());
|
||||
VolumeInfo vol = volFactory.getVolume(volumeId);
|
||||
vol.stateTransit(Volume.Event.DestroyRequested);
|
||||
throw new CloudRuntimeException(String.format("Failed to create volume: %s", volume), e);
|
||||
} finally {
|
||||
if (!created) {
|
||||
VolumeVO finalVolume = volume;
|
||||
logger.trace("Decrementing volume resource count for account {} as volume failed to create on the backend", () -> _accountMgr.getAccount(finalVolume.getAccountId()));
|
||||
_resourceLimitMgr.decrementVolumeResourceCount(volume.getAccountId(), cmd.getDisplayVolume(),
|
||||
_resourceLimitMgr.decrementVolumeResourceCount(volume.getAccountId(), display,
|
||||
volume.getSize(), _diskOfferingDao.findByIdIncludingRemoved(volume.getDiskOfferingId()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -273,8 +273,8 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
}
|
||||
}
|
||||
|
||||
private ImageTransferVO createDownloadImageTransfer(CreateImageTransferCmd cmd, VolumeVO volume) {
|
||||
Long backupId = cmd.getBackupId();
|
||||
private ImageTransferVO createDownloadImageTransfer(Long backupId, VolumeVO volume) {
|
||||
final String direction = ImageTransfer.Direction.download.toString();
|
||||
BackupVO backup = backupDao.findById(backupId);
|
||||
if (backup == null) {
|
||||
throw new CloudRuntimeException("Backup not found: " + backupId);
|
||||
|
|
@ -288,7 +288,7 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
host.getPrivateIpAddress(),
|
||||
volume.getUuid(),
|
||||
backup.getNbdPort(),
|
||||
cmd.getDirection().toString()
|
||||
direction
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
@ -339,7 +339,8 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
return hosts.get(0);
|
||||
}
|
||||
|
||||
private ImageTransferVO createUploadImageTransfer(CreateImageTransferCmd cmd, VolumeVO volume) {
|
||||
private ImageTransferVO createUploadImageTransfer(VolumeVO volume) {
|
||||
final String direction = ImageTransfer.Direction.upload.toString();
|
||||
String transferId = UUID.randomUUID().toString();
|
||||
|
||||
int nbdPort = allocateNbdPort();
|
||||
|
|
@ -356,7 +357,7 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
volume.getUuid(),
|
||||
volumePath,
|
||||
nbdPort,
|
||||
cmd.getDirection().toString()
|
||||
direction
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
@ -374,14 +375,14 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
host.getPrivateIpAddress(),
|
||||
volume.getUuid(),
|
||||
nbdPort,
|
||||
cmd.getDirection().toString()
|
||||
direction
|
||||
);
|
||||
|
||||
EndPoint ssvm = _epSelector.findSsvm(volume.getDataCenterId());
|
||||
transferAnswer = (CreateImageTransferAnswer) ssvm.sendMessage(transferCmd);
|
||||
|
||||
if (!transferAnswer.getResult()) {
|
||||
StopNBDServerCommand stopNbdServerCommand = new StopNBDServerCommand(transferId, cmd.getDirection().toString(), nbdPort);
|
||||
StopNBDServerCommand stopNbdServerCommand = new StopNBDServerCommand(transferId, direction, nbdPort);
|
||||
throw new CloudRuntimeException("Failed to create image transfer: " + transferAnswer.getDetails());
|
||||
}
|
||||
|
||||
|
|
@ -407,26 +408,33 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme
|
|||
|
||||
@Override
|
||||
public ImageTransferResponse createImageTransfer(CreateImageTransferCmd cmd) {
|
||||
ImageTransfer imageTransfer = createImageTransfer(cmd.getVolumeId(), cmd.getBackupId(), cmd.getDirection());
|
||||
if (imageTransfer instanceof ImageTransferVO) {
|
||||
ImageTransferVO imageTransferVO = (ImageTransferVO) imageTransfer;
|
||||
return toImageTransferResponse(imageTransferVO);
|
||||
}
|
||||
return toImageTransferResponse(imageTransferDao.findById(imageTransfer.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageTransfer createImageTransfer(long volumeId, Long backupId, ImageTransfer.Direction direction) {
|
||||
ImageTransfer imageTransfer;
|
||||
Long volumeId = cmd.getVolumeId();
|
||||
VolumeVO volume = volumeDao.findById(cmd.getVolumeId());
|
||||
VolumeVO volume = volumeDao.findById(volumeId);
|
||||
|
||||
ImageTransferVO existingTransfer = imageTransferDao.findByVolume(volume.getId());
|
||||
if (existingTransfer != null) {
|
||||
throw new CloudRuntimeException("Image transfer already in progress for volume: " + volume.getUuid());
|
||||
}
|
||||
|
||||
if (cmd.getDirection().equals(ImageTransfer.Direction.upload)) {
|
||||
imageTransfer = createUploadImageTransfer(cmd, volume);
|
||||
} else if (cmd.getDirection().equals(ImageTransfer.Direction.download)) {
|
||||
imageTransfer = createDownloadImageTransfer(cmd, volume);
|
||||
if (ImageTransfer.Direction.upload.equals(direction)) {
|
||||
imageTransfer = createUploadImageTransfer(volume);
|
||||
} else if (ImageTransfer.Direction.download.equals(direction)) {
|
||||
imageTransfer = createDownloadImageTransfer(backupId, volume);
|
||||
} else {
|
||||
throw new CloudRuntimeException("Invalid direction: " + cmd.getDirection());
|
||||
throw new CloudRuntimeException("Invalid direction: " + direction);
|
||||
}
|
||||
|
||||
ImageTransferVO imageTransferVO = imageTransferDao.findById(imageTransfer.getId());
|
||||
ImageTransferResponse response = toImageTransferResponse(imageTransferVO);
|
||||
return response;
|
||||
return imageTransferDao.findById(imageTransfer.getId());
|
||||
}
|
||||
|
||||
private void finalizeDownloadImageTransfer(ImageTransferVO imageTransfer) {
|
||||
|
|
|
|||
|
|
@ -597,26 +597,22 @@ public class VolumeApiServiceImplTest {
|
|||
|
||||
@Test
|
||||
public void testNullGetVolumeNameFromCmd() {
|
||||
when(createVol.getVolumeName()).thenReturn(null);
|
||||
Assert.assertNotNull(volumeApiServiceImpl.getVolumeNameFromCommand(createVol));
|
||||
Assert.assertNotNull(volumeApiServiceImpl.getVolumeNameFromCommand(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyGetVolumeNameFromCmd() {
|
||||
when(createVol.getVolumeName()).thenReturn("");
|
||||
Assert.assertNotNull(volumeApiServiceImpl.getVolumeNameFromCommand(createVol));
|
||||
Assert.assertNotNull(volumeApiServiceImpl.getVolumeNameFromCommand(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlankGetVolumeNameFromCmd() {
|
||||
when(createVol.getVolumeName()).thenReturn(" ");
|
||||
Assert.assertNotNull(volumeApiServiceImpl.getVolumeNameFromCommand(createVol));
|
||||
Assert.assertNotNull(volumeApiServiceImpl.getVolumeNameFromCommand(" "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonEmptyGetVolumeNameFromCmd() {
|
||||
when(createVol.getVolumeName()).thenReturn("abc");
|
||||
Assert.assertSame(volumeApiServiceImpl.getVolumeNameFromCommand(createVol), "abc");
|
||||
Assert.assertSame(volumeApiServiceImpl.getVolumeNameFromCommand("abc"), "abc");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -54,8 +54,6 @@ import java.util.stream.Stream;
|
|||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.agent.api.ConvertSnapshotCommand;
|
||||
|
||||
import org.apache.cloudstack.backup.CreateImageTransferAnswer;
|
||||
import org.apache.cloudstack.backup.CreateImageTransferCommand;
|
||||
import org.apache.cloudstack.backup.FinalizeImageTransferCommand;
|
||||
|
|
@ -101,8 +99,8 @@ import org.apache.http.client.HttpClient;
|
|||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
|
||||
|
|
@ -112,6 +110,7 @@ import com.cloud.agent.api.CheckHealthAnswer;
|
|||
import com.cloud.agent.api.CheckHealthCommand;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.ComputeChecksumCommand;
|
||||
import com.cloud.agent.api.ConvertSnapshotCommand;
|
||||
import com.cloud.agent.api.DeleteSnapshotsDirCommand;
|
||||
import com.cloud.agent.api.GetStorageStatsAnswer;
|
||||
import com.cloud.agent.api.GetStorageStatsCommand;
|
||||
|
|
@ -3827,7 +3826,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
|||
// Open firewall port for image server
|
||||
if (_inSystemVM) {
|
||||
String rule = String.format("-p tcp -m state --state NEW -m tcp --dport %d -j ACCEPT", imageServerPort);
|
||||
IpTablesHelper.addConditionally(IpTablesHelper.INPUT_CHAIN, true, rule,
|
||||
IpTablesHelper.addConditionally(IpTablesHelper.INPUT_CHAIN, true, rule,
|
||||
String.format("Error in opening up image server port %d", imageServerPort));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue