mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
api,server,ui: snapshot copy, multi-zone replica (#7873)
This PR adds new functionality to copy snapshots across zones and take snapshots for multiple zones. Copy functionality is similar to template copy. The source zone acts as the web server from where the destination zone(s) can download the snapshot files. For this purpose, a new API - `copySnapshot` has been added. The response for copySnapshot will be returning zone and download details from the first destination zone of the request. This behaviour is similar to the `copyTemplate` API. In a similar manner, multiple zones can be selected while taking the snapshots or creating snapshot policies. For this snapshot will be taken in the base zone(in which volume is present) and then copied to the additional zones. A new parameter - `zoneids` has been added to `createSnapshot` and `createSnapshotPolicy` APIs. As snapshots can be present on multiple zones (secondary stores), a new parameter `zoneid` has been added to delete the snapshot copy on a specific zone. `listSnapshots` API has been updated to allow listing snapshot entries for different zones/datastores. New parameters - `showUnique`, `locationType` have been added. Events generated during snapshot operations will now be linked to the snapshot itself rather than the volume of the snapshot. `listSnapshotPolicies` and `createSnapshotPolicy` APIs will return zone details of the zones in which backup will be scheduled for the policy. ---- New API added `copySnapshot` Request and response params updated for APIs ``` - listSnapshots - deleteSnapshot - createTemplate - listZones - listSnapshotPolicies - createSnapshotPolicy ``` UI updated for - Snapshot detail view - Create snapshot form - Create snapshot policy form - Create volume (from snapshot) form - Create template (from snapshot) form Doc PR: https://github.com/apache/cloudstack-documentation/pull/344 PR: https://github.com/apache/cloudstack/pull/7873
This commit is contained in:
parent
99ded8169b
commit
543c54c718
@ -30,6 +30,7 @@ import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.config.Configuration;
|
||||
import org.apache.cloudstack.ha.HAConfig;
|
||||
import org.apache.cloudstack.usage.Usage;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.DataCenterGuestIpv6Prefix;
|
||||
@ -84,7 +85,6 @@ import com.cloud.user.User;
|
||||
import com.cloud.vm.Nic;
|
||||
import com.cloud.vm.NicSecondaryIp;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
|
||||
public class EventTypes {
|
||||
|
||||
@ -320,6 +320,7 @@ public class EventTypes {
|
||||
public static final String EVENT_DOMAIN_UPDATE = "DOMAIN.UPDATE";
|
||||
|
||||
// Snapshots
|
||||
public static final String EVENT_SNAPSHOT_COPY = "SNAPSHOT.COPY";
|
||||
public static final String EVENT_SNAPSHOT_CREATE = "SNAPSHOT.CREATE";
|
||||
public static final String EVENT_SNAPSHOT_ON_PRIMARY = "SNAPSHOT.ON_PRIMARY";
|
||||
public static final String EVENT_SNAPSHOT_OFF_PRIMARY = "SNAPSHOT.OFF_PRIMARY";
|
||||
|
||||
@ -19,9 +19,9 @@
|
||||
package com.cloud.storage;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
|
||||
import org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd;
|
||||
@ -37,6 +37,7 @@ import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
|
||||
public interface VolumeApiService {
|
||||
|
||||
@ -105,10 +106,10 @@ public interface VolumeApiService {
|
||||
|
||||
Volume detachVolumeFromVM(DetachVolumeCmd cmd);
|
||||
|
||||
Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map<String, String> tags)
|
||||
Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map<String, String> tags, List<Long> zoneIds)
|
||||
throws ResourceAllocationException;
|
||||
|
||||
Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType) throws ResourceAllocationException;
|
||||
Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List<Long> zoneIds) throws ResourceAllocationException;
|
||||
|
||||
Volume updateVolume(long volumeId, String path, String state, Long storageId, Boolean displayVolume, String customId, long owner, String chainInfo, String name);
|
||||
|
||||
|
||||
@ -18,18 +18,20 @@ package com.cloud.storage.snapshot;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.api.command.user.snapshot.CopySnapshotCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotPolicyCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.DeleteSnapshotPoliciesCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotPoliciesCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.UpdateSnapshotPolicyCmd;
|
||||
|
||||
import com.cloud.api.commands.ListRecurringSnapshotScheduleCmd;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.StorageUnavailableException;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.Pair;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.UpdateSnapshotPolicyCmd;
|
||||
|
||||
public interface SnapshotApiService {
|
||||
|
||||
@ -50,7 +52,7 @@ public interface SnapshotApiService {
|
||||
* @param snapshotId
|
||||
* TODO
|
||||
*/
|
||||
boolean deleteSnapshot(long snapshotId);
|
||||
boolean deleteSnapshot(long snapshotId, Long zoneId);
|
||||
|
||||
/**
|
||||
* Creates a policy with specified schedule. maxSnaps specifies the number of most recent snapshots that are to be
|
||||
@ -88,7 +90,7 @@ public interface SnapshotApiService {
|
||||
|
||||
Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType) throws ResourceAllocationException;
|
||||
|
||||
Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, Boolean isFromVmSnapshot)
|
||||
Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, Boolean isFromVmSnapshot, List<Long> zoneIds)
|
||||
throws ResourceAllocationException;
|
||||
|
||||
|
||||
@ -124,4 +126,6 @@ public interface SnapshotApiService {
|
||||
SnapshotPolicy updateSnapshotPolicy(UpdateSnapshotPolicyCmd updateSnapshotPolicyCmd);
|
||||
|
||||
void markVolumeSnapshotsAsDestroyed(Volume volume);
|
||||
|
||||
Snapshot copySnapshot(CopySnapshotCmd cmd) throws StorageUnavailableException, ResourceAllocationException;
|
||||
}
|
||||
|
||||
@ -76,8 +76,10 @@ public class ApiConstants {
|
||||
public static final String CSR = "csr";
|
||||
public static final String PRIVATE_KEY = "privatekey";
|
||||
public static final String DATASTORE_HOST = "datastorehost";
|
||||
public static final String DATASTORE_ID = "datastoreid";
|
||||
public static final String DATASTORE_NAME = "datastorename";
|
||||
public static final String DATASTORE_PATH = "datastorepath";
|
||||
public static final String DATASTORE_STATE = "datastorestate";
|
||||
public static final String DATASTORE_TYPE = "datastoretype";
|
||||
public static final String DOMAIN_SUFFIX = "domainsuffix";
|
||||
public static final String DNS_SEARCH_ORDER = "dnssearchorder";
|
||||
@ -492,6 +494,7 @@ public class ApiConstants {
|
||||
public static final String ZONE = "zone";
|
||||
public static final String ZONE_ID = "zoneid";
|
||||
public static final String ZONE_NAME = "zonename";
|
||||
public static final String ZONE_WISE = "zonewise";
|
||||
public static final String NETWORK_TYPE = "networktype";
|
||||
public static final String PAGE = "page";
|
||||
public static final String PAGE_SIZE = "pagesize";
|
||||
|
||||
@ -0,0 +1,181 @@
|
||||
// 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.api.command.user.snapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ResponseObject;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.command.user.UserCmd;
|
||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.exception.StorageUnavailableException;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.user.Account;
|
||||
|
||||
@APICommand(name = "copySnapshot", description = "Copies a snapshot from one zone to another.",
|
||||
responseObject = SnapshotResponse.class, responseView = ResponseObject.ResponseView.Restricted,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd {
|
||||
public static final Logger s_logger = Logger.getLogger(CopySnapshotCmd.class.getName());
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// API parameters /////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID,
|
||||
entityType = SnapshotResponse.class, required = true, description = "the ID of the snapshot.")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.SOURCE_ZONE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = ZoneResponse.class,
|
||||
description = "The ID of the zone in which the snapshot is currently present. " +
|
||||
"If not specified then the zone of snapshot's volume will be used.")
|
||||
private Long sourceZoneId;
|
||||
|
||||
@Parameter(name = ApiConstants.DESTINATION_ZONE_ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = ZoneResponse.class,
|
||||
required = false,
|
||||
description = "The ID of the zone the snapshot is being copied to.")
|
||||
protected Long destZoneId;
|
||||
|
||||
@Parameter(name = ApiConstants.DESTINATION_ZONE_ID_LIST,
|
||||
type=CommandType.LIST,
|
||||
collectionType = CommandType.UUID,
|
||||
entityType = ZoneResponse.class,
|
||||
required = false,
|
||||
description = "A comma-separated list of IDs of the zones that the snapshot needs to be copied to. " +
|
||||
"Specify this list if the snapshot needs to copied to multiple zones in one go. " +
|
||||
"Do not specify destzoneid and destzoneids together, however one of them is required.")
|
||||
protected List<Long> destZoneIds;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Long getSourceZoneId() {
|
||||
return sourceZoneId;
|
||||
}
|
||||
|
||||
public List<Long> getDestinationZoneIds() {
|
||||
if (destZoneIds != null && destZoneIds.size() != 0) {
|
||||
return destZoneIds;
|
||||
}
|
||||
if (destZoneId != null) {
|
||||
List < Long > destIds = new ArrayList<>();
|
||||
destIds.add(destZoneId);
|
||||
return destIds;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType() {
|
||||
return EventTypes.EVENT_SNAPSHOT_COPY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventDescription() {
|
||||
StringBuilder descBuilder = new StringBuilder();
|
||||
if (getDestinationZoneIds() != null) {
|
||||
for (Long destId : getDestinationZoneIds()) {
|
||||
descBuilder.append(", ");
|
||||
descBuilder.append(_uuidMgr.getUuid(DataCenter.class, destId));
|
||||
}
|
||||
if (descBuilder.length() > 0) {
|
||||
descBuilder.deleteCharAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
return "copying snapshot: " + _uuidMgr.getUuid(Snapshot.class, getId()) + ((descBuilder.length() > 0) ? " to zones: " + descBuilder.toString() : "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.Snapshot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getApiResourceId() {
|
||||
return getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
Snapshot snapshot = _entityMgr.findById(Snapshot.class, getId());
|
||||
if (snapshot != null) {
|
||||
return snapshot.getAccountId();
|
||||
}
|
||||
return Account.ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException {
|
||||
try {
|
||||
if (destZoneId == null && CollectionUtils.isEmpty(destZoneIds))
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
|
||||
"Either destzoneid or destzoneids parameters have to be specified.");
|
||||
|
||||
if (destZoneId != null && CollectionUtils.isNotEmpty(destZoneIds))
|
||||
throw new ServerApiException(ApiErrorCode.PARAM_ERROR,
|
||||
"Both destzoneid and destzoneids cannot be specified at the same time.");
|
||||
|
||||
CallContext.current().setEventDetails(getEventDescription());
|
||||
Snapshot snapshot = _snapshotService.copySnapshot(this);
|
||||
|
||||
if (snapshot != null) {
|
||||
SnapshotResponse response = _queryService.listSnapshot(this);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
} else {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to copy snapshot");
|
||||
}
|
||||
} catch (StorageUnavailableException ex) {
|
||||
s_logger.warn("Exception: ", ex);
|
||||
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
|
||||
} catch (ResourceAllocationException ex) {
|
||||
s_logger.warn("Exception: ", ex);
|
||||
throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.user.snapshot;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
@ -32,6 +33,7 @@ import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.SnapshotPolicyResponse;
|
||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@ -90,6 +92,15 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd {
|
||||
@Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, description = "Map of tags (key/value pairs)")
|
||||
private Map tags;
|
||||
|
||||
@Parameter(name = ApiConstants.ZONE_ID_LIST,
|
||||
type=CommandType.LIST,
|
||||
collectionType = CommandType.UUID,
|
||||
entityType = ZoneResponse.class,
|
||||
description = "A comma-separated list of IDs of the zones in which the snapshot will be made available. " +
|
||||
"The snapshot will always be made available in the zone in which the volume is present.",
|
||||
since = "4.19.0")
|
||||
protected List<Long> zoneIds;
|
||||
|
||||
private String syncObjectType = BaseAsyncCmd.snapshotHostSyncObject;
|
||||
|
||||
// ///////////////////////////////////////////////////
|
||||
@ -148,6 +159,10 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd {
|
||||
return _snapshotService.getHostIdForSnapshotOperation(volume);
|
||||
}
|
||||
|
||||
public List<Long> getZoneIds() {
|
||||
return zoneIds;
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////
|
||||
// ///////////// API Implementation///////////////////
|
||||
// ///////////////////////////////////////////////////
|
||||
@ -196,7 +211,7 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd {
|
||||
|
||||
@Override
|
||||
public void create() throws ResourceAllocationException {
|
||||
Snapshot snapshot = _volumeService.allocSnapshot(getVolumeId(), getPolicyId(), getSnapshotName(), getLocationType());
|
||||
Snapshot snapshot = _volumeService.allocSnapshot(getVolumeId(), getPolicyId(), getSnapshotName(), getLocationType(), getZoneIds());
|
||||
if (snapshot != null) {
|
||||
setEntityId(snapshot.getId());
|
||||
setEntityUuid(snapshot.getUuid());
|
||||
@ -210,7 +225,7 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd {
|
||||
Snapshot snapshot;
|
||||
try {
|
||||
snapshot =
|
||||
_volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup(), getTags());
|
||||
_volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup(), getTags(), getZoneIds());
|
||||
|
||||
if (snapshot != null) {
|
||||
SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot);
|
||||
|
||||
@ -186,7 +186,7 @@ public class CreateSnapshotFromVMSnapshotCmd extends BaseAsyncCreateCmd {
|
||||
} finally {
|
||||
if (snapshot == null) {
|
||||
try {
|
||||
_snapshotService.deleteSnapshot(getEntityId());
|
||||
_snapshotService.deleteSnapshot(getEntityId(), null);
|
||||
} catch (Exception e) {
|
||||
s_logger.debug("Failed to clean failed snapshot" + getEntityId());
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.user.snapshot;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
@ -30,6 +31,7 @@ import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.SnapshotPolicyResponse;
|
||||
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@ -75,6 +77,14 @@ public class CreateSnapshotPolicyCmd extends BaseCmd {
|
||||
@Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, description = "Map of tags (key/value pairs)")
|
||||
private Map tags;
|
||||
|
||||
@Parameter(name = ApiConstants.ZONE_ID_LIST,
|
||||
type=CommandType.LIST,
|
||||
collectionType = CommandType.UUID,
|
||||
entityType = ZoneResponse.class,
|
||||
description = "A list of IDs of the zones in which the snapshots will be made available." +
|
||||
"The snapshots will always be made available in the zone in which the volume is present.")
|
||||
protected List<Long> zoneIds;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -107,6 +117,10 @@ public class CreateSnapshotPolicyCmd extends BaseCmd {
|
||||
return display;
|
||||
}
|
||||
|
||||
public List<Long> getZoneIds() {
|
||||
return zoneIds;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.snapshot;
|
||||
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
@ -48,6 +49,10 @@ public class DeleteSnapshotCmd extends BaseAsyncCmd {
|
||||
@Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = SnapshotResponse.class,
|
||||
required=true, description="The ID of the snapshot")
|
||||
private Long id;
|
||||
@Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class,
|
||||
description="The ID of the zone for the snapshot", since = "4.19.0")
|
||||
private Long zoneId;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
@ -57,6 +62,10 @@ public class DeleteSnapshotCmd extends BaseAsyncCmd {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -94,7 +103,7 @@ public class DeleteSnapshotCmd extends BaseAsyncCmd {
|
||||
@Override
|
||||
public void execute() {
|
||||
CallContext.current().setEventDetails("Snapshot Id: " + this._uuidMgr.getUuid(Snapshot.class, getId()));
|
||||
boolean result = _snapshotService.deleteSnapshot(getId());
|
||||
boolean result = _snapshotService.deleteSnapshot(getId(), getZoneId());
|
||||
if (result) {
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
setResponseObject(response);
|
||||
|
||||
@ -16,11 +16,8 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.snapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
@ -30,9 +27,9 @@ import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.utils.Pair;
|
||||
|
||||
@APICommand(name = "listSnapshots", description = "Lists all available snapshots for the account.", responseObject = SnapshotResponse.class, entityType = {
|
||||
Snapshot.class }, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
@ -65,6 +62,13 @@ public class ListSnapshotsCmd extends BaseListTaggedResourcesCmd {
|
||||
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "list snapshots by zone id")
|
||||
private Long zoneId;
|
||||
|
||||
@Parameter(name = ApiConstants.SHOW_UNIQUE, type = CommandType.BOOLEAN, description = "If set to false, list templates across zones and their storages", since = "4.19.0")
|
||||
private Boolean showUnique;
|
||||
|
||||
@Parameter(name = ApiConstants.LOCATION_TYPE, type = CommandType.STRING, description = "list snapshots by location type. Used only when showunique=false. " +
|
||||
"Valid location types: 'primary', 'secondary'. Default is empty", since = "4.19.0")
|
||||
private String locationType;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -93,6 +97,20 @@ public class ListSnapshotsCmd extends BaseListTaggedResourcesCmd {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public boolean isShowUnique() {
|
||||
if (Boolean.FALSE.equals(showUnique)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getLocationType() {
|
||||
if (!isShowUnique()) {
|
||||
return locationType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -104,15 +122,7 @@ public class ListSnapshotsCmd extends BaseListTaggedResourcesCmd {
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
Pair<List<? extends Snapshot>, Integer> result = _snapshotService.listSnapshots(this);
|
||||
ListResponse<SnapshotResponse> response = new ListResponse<SnapshotResponse>();
|
||||
List<SnapshotResponse> snapshotResponses = new ArrayList<SnapshotResponse>();
|
||||
for (Snapshot snapshot : result.first()) {
|
||||
SnapshotResponse snapshotResponse = _responseGenerator.createSnapshotResponse(snapshot);
|
||||
snapshotResponse.setObjectName("snapshot");
|
||||
snapshotResponses.add(snapshotResponse);
|
||||
}
|
||||
response.setResponses(snapshotResponses, result.second());
|
||||
ListResponse<SnapshotResponse> response = _queryService.listSnapshots(this);
|
||||
response.setResponseName(getCommandName());
|
||||
|
||||
setResponseObject(response);
|
||||
|
||||
@ -29,6 +29,7 @@ import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@ -135,6 +136,9 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd {
|
||||
@Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "create template for the project")
|
||||
private Long projectId;
|
||||
|
||||
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the zone for the template. Can be specified with snapshot only", since = "4.19.0")
|
||||
private Long zoneId;
|
||||
|
||||
// ///////////////////////////////////////////////////
|
||||
// ///////////////// Accessors ///////////////////////
|
||||
// ///////////////////////////////////////////////////
|
||||
@ -209,6 +213,10 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd {
|
||||
return isDynamicallyScalable == null ? false : isDynamicallyScalable;
|
||||
}
|
||||
|
||||
public Long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
// ///////////////////////////////////////////////////
|
||||
// ///////////// API Implementation///////////////////
|
||||
// ///////////////////////////////////////////////////
|
||||
|
||||
@ -16,10 +16,9 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.zone;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseListCmd;
|
||||
@ -30,6 +29,7 @@ import org.apache.cloudstack.api.command.user.UserCmd;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@APICommand(name = "listZones", description = "Lists zones", responseObject = ZoneResponse.class, responseView = ResponseView.Restricted,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
@ -44,6 +44,9 @@ public class ListZonesCmd extends BaseListCmd implements UserCmd {
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the ID of the zone")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ZoneResponse.class, description = "the IDs of the zones, mutually exclusive with id", since = "4.19.0")
|
||||
private List<Long> ids;
|
||||
|
||||
@Parameter(name = ApiConstants.AVAILABLE,
|
||||
type = CommandType.BOOLEAN,
|
||||
description = "true if you want to retrieve all available Zones. False if you only want to return the Zones"
|
||||
@ -76,6 +79,10 @@ public class ListZonesCmd extends BaseListCmd implements UserCmd {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<Long> getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public Boolean isAvailable() {
|
||||
return available;
|
||||
}
|
||||
|
||||
@ -58,8 +58,13 @@ public class SnapshotPolicyResponse extends BaseResponseWithTagInformation {
|
||||
@Param(description = "is this policy for display to the regular user", since = "4.4", authorized = {RoleType.Admin})
|
||||
private Boolean forDisplay;
|
||||
|
||||
@SerializedName(ApiConstants.ZONE)
|
||||
@Param(description = "The list of zones in which snapshot backup is scheduled", responseObject = ZoneResponse.class, since = "4.19.0")
|
||||
protected Set<ZoneResponse> zones;
|
||||
|
||||
public SnapshotPolicyResponse() {
|
||||
tags = new LinkedHashSet<ResourceTagResponse>();
|
||||
zones = new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
@ -121,4 +126,8 @@ public class SnapshotPolicyResponse extends BaseResponseWithTagInformation {
|
||||
public void setTags(Set<ResourceTagResponse> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public void setZones(Set<ZoneResponse> zones) {
|
||||
this.zones = zones;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ package org.apache.cloudstack.api.response;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
@ -29,7 +30,7 @@ import com.cloud.storage.Snapshot;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
@EntityReference(value = Snapshot.class)
|
||||
public class SnapshotResponse extends BaseResponseWithTagInformation implements ControlledEntityResponse {
|
||||
public class SnapshotResponse extends BaseResponseWithTagInformation implements ControlledViewEntityResponse {
|
||||
@SerializedName(ApiConstants.ID)
|
||||
@Param(description = "ID of the snapshot")
|
||||
private String id;
|
||||
@ -90,6 +91,10 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements
|
||||
@Param(description = "the state of the snapshot. BackedUp means that snapshot is ready to be used; Creating - the snapshot is being allocated on the primary storage; BackingUp - the snapshot is being backed up on secondary storage")
|
||||
private Snapshot.State state;
|
||||
|
||||
@SerializedName(ApiConstants.STATUS)
|
||||
@Param(description = "the status of the template")
|
||||
private String status;
|
||||
|
||||
@SerializedName(ApiConstants.PHYSICAL_SIZE)
|
||||
@Param(description = "physical size of backedup snapshot on image store")
|
||||
private long physicalSize;
|
||||
@ -98,6 +103,10 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements
|
||||
@Param(description = "id of the availability zone")
|
||||
private String zoneId;
|
||||
|
||||
@SerializedName(ApiConstants.ZONE_NAME)
|
||||
@Param(description = "name of the availability zone")
|
||||
private String zoneName;
|
||||
|
||||
@SerializedName(ApiConstants.REVERTABLE)
|
||||
@Param(description = "indicates whether the underlying storage supports reverting the volume to this snapshot")
|
||||
private boolean revertable;
|
||||
@ -114,6 +123,26 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements
|
||||
@Param(description = "virtual size of backedup snapshot on image store")
|
||||
private long virtualSize;
|
||||
|
||||
@SerializedName(ApiConstants.DATASTORE_ID)
|
||||
@Param(description = "ID of the datastore for the snapshot entry", since = "4.19.0")
|
||||
private String datastoreId;
|
||||
|
||||
@SerializedName(ApiConstants.DATASTORE_NAME)
|
||||
@Param(description = "name of the datastore for the snapshot entry", since = "4.19.0")
|
||||
private String datastoreName;
|
||||
|
||||
@SerializedName(ApiConstants.DATASTORE_STATE)
|
||||
@Param(description = "state of the snapshot on the datastore", since = "4.19.0")
|
||||
private String datastoreState;
|
||||
|
||||
@SerializedName(ApiConstants.DATASTORE_TYPE)
|
||||
@Param(description = "type of the datastore for the snapshot entry", since = "4.19.0")
|
||||
private String datastoreType;
|
||||
|
||||
@SerializedName(ApiConstants.DOWNLOAD_DETAILS)
|
||||
@Param(description = "download progress of a snapshot", since = "4.19.0")
|
||||
private Map<String, String> downloadDetails;
|
||||
|
||||
public SnapshotResponse() {
|
||||
tags = new LinkedHashSet<ResourceTagResponse>();
|
||||
}
|
||||
@ -190,7 +219,11 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void setPhysicaSize(long physicalSize) {
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public void setPhysicalSize(long physicalSize) {
|
||||
this.physicalSize = physicalSize;
|
||||
}
|
||||
|
||||
@ -208,6 +241,10 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements
|
||||
this.zoneId = zoneId;
|
||||
}
|
||||
|
||||
public void setZoneName(String zoneName) {
|
||||
this.zoneName = zoneName;
|
||||
}
|
||||
|
||||
public void setTags(Set<ResourceTagResponse> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
@ -231,4 +268,24 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements
|
||||
public void setVirtualSize(long virtualSize) {
|
||||
this.virtualSize = virtualSize;
|
||||
}
|
||||
|
||||
public void setDatastoreId(String datastoreId) {
|
||||
this.datastoreId = datastoreId;
|
||||
}
|
||||
|
||||
public void setDatastoreName(String datastoreName) {
|
||||
this.datastoreName = datastoreName;
|
||||
}
|
||||
|
||||
public void setDatastoreState(String datastoreState) {
|
||||
this.datastoreState = datastoreState;
|
||||
}
|
||||
|
||||
public void setDatastoreType(String datastoreType) {
|
||||
this.datastoreType = datastoreType;
|
||||
}
|
||||
|
||||
public void setDownloadDetails(Map<String, String> downloadDetails) {
|
||||
this.downloadDetails = downloadDetails;
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
|
||||
|
||||
@SerializedName("securitygroupsenabled")
|
||||
@Param(description = "true if security groups support is enabled, false otherwise")
|
||||
private boolean securityGroupsEnabled;
|
||||
private Boolean securityGroupsEnabled;
|
||||
|
||||
@SerializedName("allocationstate")
|
||||
@Param(description = "the allocation state of the cluster")
|
||||
@ -115,7 +115,7 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
|
||||
|
||||
@SerializedName(ApiConstants.LOCAL_STORAGE_ENABLED)
|
||||
@Param(description = "true if local storage offering enabled, false otherwise")
|
||||
private boolean localStorageEnabled;
|
||||
private Boolean localStorageEnabled;
|
||||
|
||||
@SerializedName(ApiConstants.TAGS)
|
||||
@Param(description = "the list of resource tags associated with zone.", responseObject = ResourceTagResponse.class, since = "4.3")
|
||||
@ -131,7 +131,7 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
|
||||
|
||||
@SerializedName(ApiConstants.ALLOW_USER_SPECIFY_VR_MTU)
|
||||
@Param(description = "Allow end users to specify VR MTU", since = "4.18.0")
|
||||
private boolean allowUserSpecifyVRMtu;
|
||||
private Boolean allowUserSpecifyVRMtu;
|
||||
|
||||
@SerializedName(ApiConstants.ROUTER_PRIVATE_INTERFACE_MAX_MTU)
|
||||
@Param(description = "The maximum value the MTU can have on the VR's private interfaces", since = "4.18.0")
|
||||
@ -197,7 +197,7 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
|
||||
this.networkType = networkType;
|
||||
}
|
||||
|
||||
public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
|
||||
public void setSecurityGroupsEnabled(Boolean securityGroupsEnabled) {
|
||||
this.securityGroupsEnabled = securityGroupsEnabled;
|
||||
}
|
||||
|
||||
@ -221,7 +221,7 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
|
||||
this.domainName = domainName;
|
||||
}
|
||||
|
||||
public void setLocalStorageEnabled(boolean localStorageEnabled) {
|
||||
public void setLocalStorageEnabled(Boolean localStorageEnabled) {
|
||||
this.localStorageEnabled = localStorageEnabled;
|
||||
}
|
||||
|
||||
@ -241,6 +241,10 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
|
||||
this.ip6Dns2 = ip6Dns2;
|
||||
}
|
||||
|
||||
public void setTags(Set<ResourceTagResponse> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public void addTag(ResourceTagResponse tag) {
|
||||
this.tags.add(tag);
|
||||
}
|
||||
@ -345,7 +349,7 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
|
||||
return resourceIconResponse;
|
||||
}
|
||||
|
||||
public void setAllowUserSpecifyVRMtu(boolean allowUserSpecifyVRMtu) {
|
||||
public void setAllowUserSpecifyVRMtu(Boolean allowUserSpecifyVRMtu) {
|
||||
this.allowUserSpecifyVRMtu = allowUserSpecifyVRMtu;
|
||||
}
|
||||
|
||||
|
||||
@ -44,6 +44,8 @@ import org.apache.cloudstack.api.command.user.project.ListProjectInvitationsCmd;
|
||||
import org.apache.cloudstack.api.command.user.project.ListProjectsCmd;
|
||||
import org.apache.cloudstack.api.command.user.resource.ListDetailOptionsCmd;
|
||||
import org.apache.cloudstack.api.command.user.securitygroup.ListSecurityGroupsCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.CopySnapshotCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd;
|
||||
import org.apache.cloudstack.api.command.user.tag.ListTagsCmd;
|
||||
import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
|
||||
@ -73,6 +75,7 @@ import org.apache.cloudstack.api.response.ResourceTagResponse;
|
||||
import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse;
|
||||
import org.apache.cloudstack.api.response.SecurityGroupResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||
import org.apache.cloudstack.api.response.StoragePoolResponse;
|
||||
import org.apache.cloudstack.api.response.StorageTagResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
@ -179,4 +182,8 @@ public interface QueryService {
|
||||
ListResponse<ManagementServerResponse> listManagementServers(ListMgmtsCmd cmd);
|
||||
|
||||
List<RouterHealthCheckResultResponse> listRouterHealthChecks(GetRouterHealthCheckResultsCmd cmd);
|
||||
|
||||
ListResponse<SnapshotResponse> listSnapshots(ListSnapshotsCmd cmd);
|
||||
|
||||
SnapshotResponse listSnapshot(CopySnapshotCmd cmd);
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.isNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.api.ResponseGenerator;
|
||||
@ -92,7 +93,7 @@ public class CreateSnapshotCmdTest extends TestCase {
|
||||
Snapshot snapshot = Mockito.mock(Snapshot.class);
|
||||
try {
|
||||
Mockito.when(volumeApiService.takeSnapshot(nullable(Long.class), nullable(Long.class), isNull(),
|
||||
nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), nullable(Map.class))).thenReturn(snapshot);
|
||||
nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), nullable(Map.class), nullable(List.class))).thenReturn(snapshot);
|
||||
|
||||
} catch (Exception e) {
|
||||
Assert.fail("Received exception when success expected " + e.getMessage());
|
||||
@ -125,7 +126,7 @@ public class CreateSnapshotCmdTest extends TestCase {
|
||||
|
||||
try {
|
||||
Mockito.when(volumeApiService.takeSnapshot(nullable(Long.class), nullable(Long.class), nullable(Long.class),
|
||||
nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), anyObject())).thenReturn(null);
|
||||
nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), anyObject(), Mockito.anyList())).thenReturn(null);
|
||||
} catch (Exception e) {
|
||||
Assert.fail("Received exception when success expected " + e.getMessage());
|
||||
}
|
||||
@ -159,4 +160,14 @@ public class CreateSnapshotCmdTest extends TestCase {
|
||||
ReflectionTestUtils.setField(createSnapshotCmd, "tags", tagsParams);
|
||||
Assert.assertEquals(createSnapshotCmd.getTags(), expectedTags);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetZoneIds() {
|
||||
final CreateSnapshotCmd cmd = new CreateSnapshotCmd();
|
||||
List<Long> ids = List.of(400L, 500L);
|
||||
ReflectionTestUtils.setField(cmd, "zoneIds", ids);
|
||||
Assert.assertEquals(ids.size(), cmd.getZoneIds().size());
|
||||
Assert.assertEquals(ids.get(0), cmd.getZoneIds().get(0));
|
||||
Assert.assertEquals(ids.get(1), cmd.getZoneIds().get(1));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,133 @@
|
||||
// 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.api.command.user.snapshot;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||
import org.apache.cloudstack.query.QueryService;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.snapshot.SnapshotApiService;
|
||||
import com.cloud.utils.db.UUIDManager;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CopySnapshotCmdTest {
|
||||
|
||||
@Test
|
||||
public void testGetId() {
|
||||
final CopySnapshotCmd cmd = new CopySnapshotCmd();
|
||||
Long id = 100L;
|
||||
ReflectionTestUtils.setField(cmd, "id", id);
|
||||
Assert.assertEquals(id, cmd.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSourceZoneId() {
|
||||
final CopySnapshotCmd cmd = new CopySnapshotCmd();
|
||||
Long id = 200L;
|
||||
ReflectionTestUtils.setField(cmd, "sourceZoneId", id);
|
||||
Assert.assertEquals(id, cmd.getSourceZoneId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDestZoneIdWithSingleId() {
|
||||
final CopySnapshotCmd cmd = new CopySnapshotCmd();
|
||||
Long id = 300L;
|
||||
ReflectionTestUtils.setField(cmd, "destZoneId", id);
|
||||
Assert.assertEquals(1, cmd.getDestinationZoneIds().size());
|
||||
Assert.assertEquals(id, cmd.getDestinationZoneIds().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDestZoneIdWithMultipleId() {
|
||||
final CopySnapshotCmd cmd = new CopySnapshotCmd();
|
||||
List<Long> ids = List.of(400L, 500L);
|
||||
ReflectionTestUtils.setField(cmd, "destZoneIds", ids);
|
||||
Assert.assertEquals(ids.size(), cmd.getDestinationZoneIds().size());
|
||||
Assert.assertEquals(ids.get(0), cmd.getDestinationZoneIds().get(0));
|
||||
Assert.assertEquals(ids.get(1), cmd.getDestinationZoneIds().get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDestZoneIdWithBothParams() {
|
||||
final CopySnapshotCmd cmd = new CopySnapshotCmd();
|
||||
List<Long> ids = List.of(400L, 500L);
|
||||
ReflectionTestUtils.setField(cmd, "destZoneIds", ids);
|
||||
ReflectionTestUtils.setField(cmd, "destZoneId", 100L);
|
||||
Assert.assertEquals(ids.size(), cmd.getDestinationZoneIds().size());
|
||||
Assert.assertEquals(ids.get(0), cmd.getDestinationZoneIds().get(0));
|
||||
Assert.assertEquals(ids.get(1), cmd.getDestinationZoneIds().get(1));
|
||||
}
|
||||
|
||||
@Test (expected = ServerApiException.class)
|
||||
public void testExecuteWrongNoParams() {
|
||||
final CopySnapshotCmd cmd = new CopySnapshotCmd();
|
||||
try {
|
||||
cmd.execute();
|
||||
} catch (ResourceUnavailableException e) {
|
||||
Assert.fail(String.format("Exception: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test (expected = ServerApiException.class)
|
||||
public void testExecuteWrongBothParams() {
|
||||
final CopySnapshotCmd cmd = new CopySnapshotCmd();
|
||||
List<Long> ids = List.of(400L, 500L);
|
||||
ReflectionTestUtils.setField(cmd, "destZoneIds", ids);
|
||||
ReflectionTestUtils.setField(cmd, "destZoneId", 100L);
|
||||
try {
|
||||
cmd.execute();
|
||||
} catch (ResourceUnavailableException e) {
|
||||
Assert.fail(String.format("Exception: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteSuccess() {
|
||||
SnapshotApiService snapshotApiService = Mockito.mock(SnapshotApiService.class);
|
||||
QueryService queryService = Mockito.mock(QueryService.class);
|
||||
UUIDManager uuidManager = Mockito.mock(UUIDManager.class);
|
||||
final CopySnapshotCmd cmd = new CopySnapshotCmd();
|
||||
cmd._snapshotService = snapshotApiService;
|
||||
cmd._queryService = queryService;
|
||||
cmd._uuidMgr = uuidManager;
|
||||
Snapshot snapshot = Mockito.mock(Snapshot.class);
|
||||
final Long id = 100L;
|
||||
ReflectionTestUtils.setField(cmd, "destZoneId", id);
|
||||
SnapshotResponse snapshotResponse = Mockito.mock(SnapshotResponse.class);
|
||||
try {
|
||||
Mockito.when(snapshotApiService.copySnapshot(cmd)).thenReturn(snapshot);
|
||||
Mockito.when(queryService.listSnapshot(cmd)).thenReturn(snapshotResponse);
|
||||
Mockito.when(uuidManager.getUuid(DataCenter.class, id)).thenReturn(UUID.randomUUID().toString());
|
||||
cmd.execute();
|
||||
} catch (ResourceAllocationException | ResourceUnavailableException e) {
|
||||
Assert.fail(String.format("Exception: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,7 @@
|
||||
package org.apache.cloudstack.api.command.user.snapshot;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Assert;
|
||||
@ -43,4 +44,14 @@ public class CreateSnapshotPolicyCmdTest {
|
||||
ReflectionTestUtils.setField(createSnapshotPolicyCmd, "tags", tagsParams);
|
||||
Assert.assertEquals(createSnapshotPolicyCmd.getTags(), expectedTags);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetZoneIds() {
|
||||
final CreateSnapshotPolicyCmd cmd = new CreateSnapshotPolicyCmd();
|
||||
List<Long> ids = List.of(400L, 500L);
|
||||
ReflectionTestUtils.setField(cmd, "zoneIds", ids);
|
||||
Assert.assertEquals(ids.size(), cmd.getZoneIds().size());
|
||||
Assert.assertEquals(ids.get(0), cmd.getZoneIds().get(0));
|
||||
Assert.assertEquals(ids.get(1), cmd.getZoneIds().get(1));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
// 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.api.command.user.snapshot;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
public class DeleteSnapshotCmdTest {
|
||||
|
||||
@Test
|
||||
public void testGetZoneId() {
|
||||
final DeleteSnapshotCmd cmd = new DeleteSnapshotCmd();
|
||||
Long id = 400L;
|
||||
ReflectionTestUtils.setField(cmd, "zoneId", id);
|
||||
Assert.assertEquals(id, cmd.getZoneId());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
// 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.api.command.user.snapshot;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
public class ListSnapshotsCmdTest {
|
||||
|
||||
@Test
|
||||
public void testIsShowUniqueNoValue() {
|
||||
final ListSnapshotsCmd cmd = new ListSnapshotsCmd();
|
||||
Assert.assertTrue(cmd.isShowUnique());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsShowUniqueFalse() {
|
||||
final ListSnapshotsCmd cmd = new ListSnapshotsCmd();
|
||||
ReflectionTestUtils.setField(cmd, "showUnique", false);
|
||||
Assert.assertFalse(cmd.isShowUnique());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsShowUniqueTrue() {
|
||||
final ListSnapshotsCmd cmd = new ListSnapshotsCmd();
|
||||
ReflectionTestUtils.setField(cmd, "showUnique", true);
|
||||
Assert.assertTrue(cmd.isShowUnique());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLocationTypeNoUnique() {
|
||||
final ListSnapshotsCmd cmd = new ListSnapshotsCmd();
|
||||
ReflectionTestUtils.setField(cmd, "locationType", "primary");
|
||||
Assert.assertNull(cmd.getLocationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLocationTypeUnique() {
|
||||
final ListSnapshotsCmd cmd = new ListSnapshotsCmd();
|
||||
ReflectionTestUtils.setField(cmd, "showUnique", false);
|
||||
String value = "secondary";
|
||||
ReflectionTestUtils.setField(cmd, "locationType", value);
|
||||
Assert.assertEquals(value, cmd.getLocationType());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
// 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.api.command.user.template;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
public class CreateTemplateCmdTest {
|
||||
|
||||
@Test
|
||||
public void testGetZoneId() {
|
||||
final CreateTemplateCmd cmd = new CreateTemplateCmd();
|
||||
Long id = 400L;
|
||||
ReflectionTestUtils.setField(cmd, "zoneId", id);
|
||||
Assert.assertEquals(id, cmd.getZoneId());
|
||||
}
|
||||
}
|
||||
@ -32,12 +32,20 @@ import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import com.cloud.utils.HumanReadableJson;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.BadCommand;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.SecStorageFirewallCfgCommand.PortConfig;
|
||||
import com.cloud.exception.UnsupportedVersionException;
|
||||
import com.cloud.serializer.GsonHelper;
|
||||
import com.cloud.utils.HumanReadableJson;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
@ -49,16 +57,6 @@ import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.BadCommand;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.SecStorageFirewallCfgCommand.PortConfig;
|
||||
import com.cloud.exception.UnsupportedVersionException;
|
||||
import com.cloud.serializer.GsonHelper;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
/**
|
||||
* Request is a simple wrapper around command and answer to add sequencing,
|
||||
* versioning, and flags. Note that the version here represents the changes
|
||||
@ -253,6 +251,7 @@ public class Request {
|
||||
jsonReader.setLenient(true);
|
||||
_cmds = s_gson.fromJson(jsonReader, (Type)Command[].class);
|
||||
} catch (JsonParseException e) {
|
||||
s_logger.error("Caught problem while parsing JSON command " + _content, e);
|
||||
_cmds = new Command[] { new BadCommand() };
|
||||
} catch (RuntimeException e) {
|
||||
s_logger.error("Caught problem with " + _content, e);
|
||||
|
||||
@ -19,23 +19,23 @@
|
||||
|
||||
package com.cloud.storage.resource;
|
||||
|
||||
import com.cloud.serializer.GsonHelper;
|
||||
import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplainceCommand;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.apache.cloudstack.storage.command.AttachCommand;
|
||||
import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplainceCommand;
|
||||
import org.apache.cloudstack.storage.command.CopyCommand;
|
||||
import org.apache.cloudstack.storage.command.CreateObjectAnswer;
|
||||
import org.apache.cloudstack.storage.command.CreateObjectCommand;
|
||||
import org.apache.cloudstack.storage.command.DeleteCommand;
|
||||
import org.apache.cloudstack.storage.command.DettachCommand;
|
||||
import org.apache.cloudstack.storage.command.IntroduceObjectCmd;
|
||||
import org.apache.cloudstack.storage.command.QuerySnapshotZoneCopyAnswer;
|
||||
import org.apache.cloudstack.storage.command.QuerySnapshotZoneCopyCommand;
|
||||
import org.apache.cloudstack.storage.command.ResignatureCommand;
|
||||
import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand;
|
||||
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
|
||||
import org.apache.cloudstack.storage.command.SyncVolumePathCommand;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
@ -43,6 +43,7 @@ import com.cloud.agent.api.to.DataObjectType;
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
import com.cloud.agent.api.to.DataTO;
|
||||
import com.cloud.agent.api.to.DiskTO;
|
||||
import com.cloud.serializer.GsonHelper;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.google.gson.Gson;
|
||||
@ -81,6 +82,8 @@ public class StorageSubsystemCommandHandlerBase implements StorageSubsystemComma
|
||||
return processor.checkDataStoreStoragePolicyCompliance((CheckDataStoreStoragePolicyComplainceCommand) command);
|
||||
} else if (command instanceof SyncVolumePathCommand) {
|
||||
return processor.syncVolumePath((SyncVolumePathCommand) command);
|
||||
} else if (command instanceof QuerySnapshotZoneCopyCommand) {
|
||||
return execute((QuerySnapshotZoneCopyCommand)command);
|
||||
}
|
||||
|
||||
return new Answer((Command)command, false, "not implemented yet");
|
||||
@ -175,6 +178,10 @@ public class StorageSubsystemCommandHandlerBase implements StorageSubsystemComma
|
||||
}
|
||||
}
|
||||
|
||||
protected Answer execute(QuerySnapshotZoneCopyCommand cmd) {
|
||||
return new QuerySnapshotZoneCopyAnswer(cmd, "Unsupported command");
|
||||
}
|
||||
|
||||
private void logCommand(Command cmd) {
|
||||
try {
|
||||
s_logger.debug(String.format("Executing command %s: [%s].", cmd.getClass().getSimpleName(), s_gogger.toJson(cmd)));
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
|
||||
package com.cloud.storage.template;
|
||||
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -27,7 +29,8 @@ import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Date;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
||||
import org.apache.cloudstack.storage.command.DownloadCommand.ResourceType;
|
||||
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
|
||||
import org.apache.commons.httpclient.Credentials;
|
||||
import org.apache.commons.httpclient.Header;
|
||||
@ -44,16 +47,12 @@ import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.httpclient.params.HttpMethodParams;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
||||
import org.apache.cloudstack.storage.command.DownloadCommand.ResourceType;
|
||||
|
||||
import com.cloud.storage.StorageLayer;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.UriUtils;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.net.Proxy;
|
||||
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
|
||||
/**
|
||||
* Download a template file using HTTP
|
||||
*
|
||||
@ -247,7 +246,9 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te
|
||||
while (!done && status != Status.ABORTED && offset <= remoteSize) {
|
||||
if ((bytes = in.read(block, 0, CHUNK_SIZE)) > -1) {
|
||||
offset = writeBlock(bytes, out, block, offset);
|
||||
if (!verifyFormat.isVerifiedFormat() && (offset >= 1048576 || offset >= remoteSize)) { //let's check format after we get 1MB or full file
|
||||
if (!ResourceType.SNAPSHOT.equals(resourceType) &&
|
||||
!verifyFormat.isVerifiedFormat() &&
|
||||
(offset >= 1048576 || offset >= remoteSize)) { //let's check format after we get 1MB or full file
|
||||
verifyFormat.invoke();
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -0,0 +1,481 @@
|
||||
// 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 com.cloud.storage.template;
|
||||
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
||||
import org.apache.cloudstack.storage.command.DownloadCommand;
|
||||
import org.apache.commons.httpclient.Header;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpException;
|
||||
import org.apache.commons.httpclient.HttpMethod;
|
||||
import org.apache.commons.httpclient.HttpMethodRetryHandler;
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
|
||||
import org.apache.commons.httpclient.NoHttpResponseException;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.httpclient.methods.HeadMethod;
|
||||
import org.apache.commons.httpclient.params.HttpMethodParams;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.storage.StorageLayer;
|
||||
|
||||
public class SimpleHttpMultiFileDownloader extends ManagedContextRunnable implements TemplateDownloader {
|
||||
public static final Logger s_logger = Logger.getLogger(SimpleHttpMultiFileDownloader.class.getName());
|
||||
private static final MultiThreadedHttpConnectionManager s_httpClientManager = new MultiThreadedHttpConnectionManager();
|
||||
|
||||
private static final int CHUNK_SIZE = 1024 * 1024; //1M
|
||||
private String[] downloadUrls;
|
||||
private String currentToFile;
|
||||
public TemplateDownloader.Status currentStatus;
|
||||
public TemplateDownloader.Status status;
|
||||
private String errorString = null;
|
||||
private long totalRemoteSize = 0;
|
||||
private long currentRemoteSize = 0;
|
||||
public long downloadTime = 0;
|
||||
public long currentTotalBytes;
|
||||
public long totalBytes = 0;
|
||||
private final HttpClient client;
|
||||
private GetMethod request;
|
||||
private boolean resume = false;
|
||||
private DownloadCompleteCallback completionCallback;
|
||||
StorageLayer _storage;
|
||||
boolean inited = true;
|
||||
|
||||
private String toDir;
|
||||
private long maxTemplateSizeInBytes;
|
||||
private DownloadCommand.ResourceType resourceType = DownloadCommand.ResourceType.TEMPLATE;
|
||||
private final HttpMethodRetryHandler retryHandler;
|
||||
|
||||
private HashMap<String, String> urlFileMap;
|
||||
|
||||
public SimpleHttpMultiFileDownloader(StorageLayer storageLayer, String[] downloadUrls, String toDir,
|
||||
DownloadCompleteCallback callback, long maxTemplateSizeInBytes,
|
||||
DownloadCommand.ResourceType resourceType) {
|
||||
_storage = storageLayer;
|
||||
this.downloadUrls = downloadUrls;
|
||||
this.toDir = toDir;
|
||||
this.resourceType = resourceType;
|
||||
this.maxTemplateSizeInBytes = maxTemplateSizeInBytes;
|
||||
completionCallback = callback;
|
||||
status = TemplateDownloader.Status.NOT_STARTED;
|
||||
currentStatus = TemplateDownloader.Status.NOT_STARTED;
|
||||
currentTotalBytes = 0;
|
||||
client = new HttpClient(s_httpClientManager);
|
||||
retryHandler = createRetryTwiceHandler();
|
||||
urlFileMap = new HashMap<>();
|
||||
}
|
||||
|
||||
private GetMethod createRequest(String downloadUrl) {
|
||||
GetMethod request = new GetMethod(downloadUrl);
|
||||
request.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, retryHandler);
|
||||
request.setFollowRedirects(true);
|
||||
return request;
|
||||
}
|
||||
|
||||
private void checkTemporaryDestination(String toDir) {
|
||||
try {
|
||||
File f = File.createTempFile("dnld", "tmp_", new File(toDir));
|
||||
if (_storage != null) {
|
||||
_storage.setWorldReadableAndWriteable(f);
|
||||
}
|
||||
currentToFile = f.getAbsolutePath();
|
||||
} catch (IOException ex) {
|
||||
errorString = "Unable to start download -- check url? ";
|
||||
currentStatus = TemplateDownloader.Status.UNRECOVERABLE_ERROR;
|
||||
s_logger.warn("Exception in constructor -- " + ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private HttpMethodRetryHandler createRetryTwiceHandler() {
|
||||
return new HttpMethodRetryHandler() {
|
||||
@Override
|
||||
public boolean retryMethod(final HttpMethod method, final IOException exception, int executionCount) {
|
||||
if (executionCount >= 2) {
|
||||
// Do not retry if over max retry count
|
||||
return false;
|
||||
}
|
||||
if (exception instanceof NoHttpResponseException) {
|
||||
// Retry if the server dropped connection on us
|
||||
return true;
|
||||
}
|
||||
if (!method.isRequestSent()) {
|
||||
// Retry if the request has not been sent fully or
|
||||
// if it's OK to retry methods that have been sent
|
||||
return true;
|
||||
}
|
||||
// otherwise do not retry
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void tryAndGetTotalRemoteSize() {
|
||||
for (String downloadUrl : downloadUrls) {
|
||||
if (StringUtils.isBlank(downloadUrl)) {
|
||||
continue;
|
||||
}
|
||||
HeadMethod headMethod = new HeadMethod(downloadUrl);
|
||||
try {
|
||||
if (client.executeMethod(headMethod) != HttpStatus.SC_OK) {
|
||||
continue;
|
||||
}
|
||||
Header contentLengthHeader = headMethod.getResponseHeader("content-length");
|
||||
if (contentLengthHeader == null) {
|
||||
continue;
|
||||
}
|
||||
totalRemoteSize += Long.parseLong(contentLengthHeader.getValue());
|
||||
} catch (IOException e) {
|
||||
s_logger.warn(String.format("Cannot reach URL: %s while trying to get remote sizes due to: %s", downloadUrl, e.getMessage()), e);
|
||||
} finally {
|
||||
headMethod.releaseConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long downloadFile(String downloadUrl) {
|
||||
s_logger.debug("Starting download for " + downloadUrl);
|
||||
currentTotalBytes = 0;
|
||||
currentRemoteSize = 0;
|
||||
File file = null;
|
||||
request = null;
|
||||
try {
|
||||
request = createRequest(downloadUrl);
|
||||
checkTemporaryDestination(toDir);
|
||||
urlFileMap.put(downloadUrl, currentToFile);
|
||||
file = new File(currentToFile);
|
||||
long localFileSize = checkLocalFileSizeForResume(resume, file);
|
||||
if (checkServerResponse(localFileSize)) return 0;
|
||||
if (!tryAndGetRemoteSize()) return 0;
|
||||
if (!canHandleDownloadSize()) return 0;
|
||||
checkAndSetDownloadSize();
|
||||
try (InputStream in = request.getResponseBodyAsStream();
|
||||
RandomAccessFile out = new RandomAccessFile(file, "rw");
|
||||
) {
|
||||
out.seek(localFileSize);
|
||||
s_logger.info("Starting download from " + downloadUrl + " to " + currentToFile + " remoteSize=" + toHumanReadableSize(currentRemoteSize) + " , max size=" + toHumanReadableSize(maxTemplateSizeInBytes));
|
||||
if (copyBytes(file, in, out)) return 0;
|
||||
checkDownloadCompletion();
|
||||
}
|
||||
return currentTotalBytes;
|
||||
} catch (HttpException hte) {
|
||||
currentStatus = TemplateDownloader.Status.UNRECOVERABLE_ERROR;
|
||||
errorString = hte.getMessage();
|
||||
} catch (IOException ioe) {
|
||||
currentStatus = TemplateDownloader.Status.UNRECOVERABLE_ERROR; //probably a file write error?
|
||||
// Let's not overwrite the original error message.
|
||||
if (errorString == null) {
|
||||
errorString = ioe.getMessage();
|
||||
}
|
||||
} finally {
|
||||
if (currentStatus == Status.UNRECOVERABLE_ERROR && file != null && file.exists() && !file.isDirectory()) {
|
||||
file.delete();
|
||||
}
|
||||
if (request != null) {
|
||||
request.releaseConnection();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long download(boolean resume, DownloadCompleteCallback callback) {
|
||||
if (skipDownloadOnStatus()) return 0;
|
||||
if (resume) {
|
||||
s_logger.error("Resume not allowed for this downloader");
|
||||
status = Status.UNRECOVERABLE_ERROR;
|
||||
return 0;
|
||||
}
|
||||
s_logger.debug("Starting downloads");
|
||||
status = Status.IN_PROGRESS;
|
||||
Date start = new Date();
|
||||
tryAndGetTotalRemoteSize();
|
||||
for (String downloadUrl : downloadUrls) {
|
||||
if (StringUtils.isBlank(downloadUrl)) {
|
||||
continue;
|
||||
}
|
||||
long bytes = downloadFile(downloadUrl);
|
||||
if (currentStatus != Status.DOWNLOAD_FINISHED) {
|
||||
break;
|
||||
}
|
||||
totalBytes += bytes;
|
||||
}
|
||||
status = currentStatus;
|
||||
Date finish = new Date();
|
||||
downloadTime += finish.getTime() - start.getTime();
|
||||
if (callback != null) {
|
||||
callback.downloadComplete(status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private boolean copyBytes(File file, InputStream in, RandomAccessFile out) throws IOException {
|
||||
int bytes;
|
||||
byte[] block = new byte[CHUNK_SIZE];
|
||||
long offset = 0;
|
||||
boolean done = false;
|
||||
currentStatus = Status.IN_PROGRESS;
|
||||
while (!done && currentStatus != Status.ABORTED && offset <= currentRemoteSize) {
|
||||
if ((bytes = in.read(block, 0, CHUNK_SIZE)) > -1) {
|
||||
offset = writeBlock(bytes, out, block, offset);
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
out.getFD().sync();
|
||||
return false;
|
||||
}
|
||||
|
||||
private long writeBlock(int bytes, RandomAccessFile out, byte[] block, long offset) throws IOException {
|
||||
out.write(block, 0, bytes);
|
||||
offset += bytes;
|
||||
out.seek(offset);
|
||||
currentTotalBytes += bytes;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private void checkDownloadCompletion() {
|
||||
String downloaded = "(incomplete download)";
|
||||
if (currentTotalBytes >= currentRemoteSize) {
|
||||
currentStatus = Status.DOWNLOAD_FINISHED;
|
||||
downloaded = "(download complete remote=" + toHumanReadableSize(currentRemoteSize) + " bytes)";
|
||||
}
|
||||
errorString = "Downloaded " + toHumanReadableSize(currentTotalBytes) + " bytes " + downloaded;
|
||||
}
|
||||
|
||||
private boolean canHandleDownloadSize() {
|
||||
if (currentRemoteSize > maxTemplateSizeInBytes) {
|
||||
s_logger.info("Remote size is too large: " + toHumanReadableSize(currentRemoteSize) + " , max=" + toHumanReadableSize(maxTemplateSizeInBytes));
|
||||
currentStatus = Status.UNRECOVERABLE_ERROR;
|
||||
errorString = "Download file size is too large";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void checkAndSetDownloadSize() {
|
||||
if (currentRemoteSize == 0) {
|
||||
currentRemoteSize = maxTemplateSizeInBytes;
|
||||
}
|
||||
if (totalRemoteSize == 0) {
|
||||
totalRemoteSize = currentRemoteSize;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean tryAndGetRemoteSize() {
|
||||
Header contentLengthHeader = request.getResponseHeader("content-length");
|
||||
boolean chunked = false;
|
||||
long reportedRemoteSize = 0;
|
||||
if (contentLengthHeader == null) {
|
||||
Header chunkedHeader = request.getResponseHeader("Transfer-Encoding");
|
||||
if (chunkedHeader == null || !"chunked".equalsIgnoreCase(chunkedHeader.getValue())) {
|
||||
currentStatus = Status.UNRECOVERABLE_ERROR;
|
||||
errorString = " Failed to receive length of download ";
|
||||
return false;
|
||||
} else if ("chunked".equalsIgnoreCase(chunkedHeader.getValue())) {
|
||||
chunked = true;
|
||||
}
|
||||
} else {
|
||||
reportedRemoteSize = Long.parseLong(contentLengthHeader.getValue());
|
||||
if (reportedRemoteSize == 0) {
|
||||
currentStatus = Status.DOWNLOAD_FINISHED;
|
||||
String downloaded = "(download complete remote=" + currentRemoteSize + "bytes)";
|
||||
errorString = "Downloaded " + currentTotalBytes + " bytes " + downloaded;
|
||||
downloadTime = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentRemoteSize == 0) {
|
||||
currentRemoteSize = reportedRemoteSize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkServerResponse(long localFileSize) throws IOException {
|
||||
int responseCode = 0;
|
||||
|
||||
if (localFileSize > 0) {
|
||||
// require partial content support for resume
|
||||
request.addRequestHeader("Range", "bytes=" + localFileSize + "-");
|
||||
if (client.executeMethod(request) != HttpStatus.SC_PARTIAL_CONTENT) {
|
||||
errorString = "HTTP Server does not support partial get";
|
||||
currentStatus = Status.UNRECOVERABLE_ERROR;
|
||||
return true;
|
||||
}
|
||||
} else if ((responseCode = client.executeMethod(request)) != HttpStatus.SC_OK) {
|
||||
currentStatus = Status.UNRECOVERABLE_ERROR;
|
||||
errorString = " HTTP Server returned " + responseCode + " (expected 200 OK) ";
|
||||
return true; //FIXME: retry?
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private long checkLocalFileSizeForResume(boolean resume, File file) {
|
||||
// TODO check the status of this downloader as well?
|
||||
long localFileSize = 0;
|
||||
if (file.exists() && resume) {
|
||||
localFileSize = file.length();
|
||||
s_logger.info("Resuming download to file (current size)=" + toHumanReadableSize(localFileSize));
|
||||
}
|
||||
return localFileSize;
|
||||
}
|
||||
|
||||
private boolean skipDownloadOnStatus() {
|
||||
switch (currentStatus) {
|
||||
case ABORTED:
|
||||
case UNRECOVERABLE_ERROR:
|
||||
case DOWNLOAD_FINISHED:
|
||||
return true;
|
||||
default:
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String[] getDownloadUrls() {
|
||||
return downloadUrls;
|
||||
}
|
||||
|
||||
public String getCurrentToFile() {
|
||||
File file = new File(currentToFile);
|
||||
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TemplateDownloader.Status getStatus() {
|
||||
return currentStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDownloadTime() {
|
||||
return downloadTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDownloadedBytes() {
|
||||
return totalBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("fallthrough")
|
||||
public boolean stopDownload() {
|
||||
switch (getStatus()) {
|
||||
case IN_PROGRESS:
|
||||
if (request != null) {
|
||||
request.abort();
|
||||
}
|
||||
currentStatus = TemplateDownloader.Status.ABORTED;
|
||||
return true;
|
||||
case UNKNOWN:
|
||||
case NOT_STARTED:
|
||||
case RECOVERABLE_ERROR:
|
||||
case UNRECOVERABLE_ERROR:
|
||||
case ABORTED:
|
||||
currentStatus = TemplateDownloader.Status.ABORTED;
|
||||
case DOWNLOAD_FINISHED:
|
||||
File f = new File(currentToFile);
|
||||
if (f.exists()) {
|
||||
f.delete();
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDownloadPercent() {
|
||||
if (totalRemoteSize == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)(100.0 * totalBytes / totalRemoteSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runInContext() {
|
||||
try {
|
||||
download(resume, completionCallback);
|
||||
} catch (Throwable t) {
|
||||
s_logger.warn("Caught exception during download " + t.getMessage(), t);
|
||||
errorString = "Failed to install: " + t.getMessage();
|
||||
currentStatus = TemplateDownloader.Status.UNRECOVERABLE_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatus(TemplateDownloader.Status status) {
|
||||
this.currentStatus = status;
|
||||
}
|
||||
|
||||
public boolean isResume() {
|
||||
return resume;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadError() {
|
||||
return errorString == null ? " " : errorString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDownloadLocalPath() {
|
||||
return toDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResume(boolean resume) {
|
||||
this.resume = resume;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxTemplateSizeInBytes() {
|
||||
return maxTemplateSizeInBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDownloadError(String error) {
|
||||
errorString = error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInited() {
|
||||
return inited;
|
||||
}
|
||||
|
||||
public DownloadCommand.ResourceType getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public Map<String, String> getDownloadedFilesMap() {
|
||||
return urlFileMap;
|
||||
}
|
||||
}
|
||||
@ -19,26 +19,25 @@
|
||||
|
||||
package com.cloud.storage.template;
|
||||
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.apache.cloudstack.storage.command.DownloadCommand.ResourceType;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
import com.cloud.storage.StorageLayer;
|
||||
import com.cloud.storage.template.Processor.FormatInfo;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
|
||||
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
||||
|
||||
public class TemplateLocation {
|
||||
private static final Logger s_logger = Logger.getLogger(TemplateLocation.class);
|
||||
public final static String Filename = "template.properties";
|
||||
@ -65,6 +64,9 @@ public class TemplateLocation {
|
||||
if (_templatePath.matches(".*" + "volumes" + ".*")) {
|
||||
_file = _storage.getFile(_templatePath + "volume.properties");
|
||||
_resourceType = ResourceType.VOLUME;
|
||||
} else if (_templatePath.matches(".*" + "snapshots" + ".*")) {
|
||||
_file = _storage.getFile(_templatePath + "snapshot.properties");
|
||||
_resourceType = ResourceType.SNAPSHOT;
|
||||
} else {
|
||||
_file = _storage.getFile(_templatePath + Filename);
|
||||
}
|
||||
@ -170,6 +172,8 @@ public class TemplateLocation {
|
||||
tmplInfo.installPath = _templatePath + _props.getProperty("filename"); // _templatePath endsWith /
|
||||
if (_resourceType == ResourceType.VOLUME) {
|
||||
tmplInfo.installPath = tmplInfo.installPath.substring(tmplInfo.installPath.indexOf("volumes"));
|
||||
} else if (_resourceType == ResourceType.SNAPSHOT) {
|
||||
tmplInfo.installPath = tmplInfo.installPath.substring(tmplInfo.installPath.indexOf("snapshots"));
|
||||
} else {
|
||||
tmplInfo.installPath = tmplInfo.installPath.substring(tmplInfo.installPath.indexOf("template"));
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
package org.apache.cloudstack.storage.command;
|
||||
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||
import org.apache.cloudstack.storage.to.TemplateObjectTO;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
|
||||
@ -33,7 +34,7 @@ import com.cloud.storage.Storage.ImageFormat;
|
||||
public class DownloadCommand extends AbstractDownloadCommand implements InternalIdentity {
|
||||
|
||||
public static enum ResourceType {
|
||||
VOLUME, TEMPLATE
|
||||
VOLUME, TEMPLATE, SNAPSHOT
|
||||
}
|
||||
|
||||
private boolean hvm;
|
||||
@ -96,6 +97,18 @@ public class DownloadCommand extends AbstractDownloadCommand implements Internal
|
||||
resourceType = ResourceType.VOLUME;
|
||||
}
|
||||
|
||||
public DownloadCommand(SnapshotObjectTO snapshot, Long maxDownloadSizeInBytes, String url) {
|
||||
super(snapshot.getName(), url, null, snapshot.getAccountId());
|
||||
_store = snapshot.getDataStore();
|
||||
installPath = snapshot.getPath();
|
||||
id = snapshot.getId();
|
||||
if (_store instanceof NfsTO) {
|
||||
setSecUrl(((NfsTO)_store).getUrl());
|
||||
}
|
||||
this.maxDownloadSizeInBytes = maxDownloadSizeInBytes;
|
||||
this.resourceType = ResourceType.SNAPSHOT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
// 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.storage.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
|
||||
public class QuerySnapshotZoneCopyAnswer extends Answer {
|
||||
private List<String> files;
|
||||
|
||||
public QuerySnapshotZoneCopyAnswer(QuerySnapshotZoneCopyCommand cmd, List<String> files) {
|
||||
super(cmd);
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
public QuerySnapshotZoneCopyAnswer(QuerySnapshotZoneCopyCommand cmd, String errMsg) {
|
||||
super(null, false, errMsg);
|
||||
}
|
||||
|
||||
public List<String> getFiles() {
|
||||
return files;
|
||||
}
|
||||
}
|
||||
@ -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.storage.command;
|
||||
|
||||
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||
|
||||
/*
|
||||
Command to get the list of snapshot files for copying a snapshot to a different zone
|
||||
*/
|
||||
|
||||
public class QuerySnapshotZoneCopyCommand extends StorageSubSystemCommand {
|
||||
|
||||
private SnapshotObjectTO snapshot;
|
||||
|
||||
public QuerySnapshotZoneCopyCommand(final SnapshotObjectTO snapshot) {
|
||||
super();
|
||||
this.snapshot = snapshot;
|
||||
}
|
||||
|
||||
public SnapshotObjectTO getSnapshot() {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public void setSnapshot(final SnapshotObjectTO snapshot) {
|
||||
this.snapshot = snapshot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExecuteInSequence(boolean inSeq) {}
|
||||
}
|
||||
@ -42,6 +42,7 @@ public class SnapshotObjectTO implements DataTO {
|
||||
private boolean quiescevm;
|
||||
private String[] parents;
|
||||
private Long physicalSize = (long) 0;
|
||||
private long accountId;
|
||||
|
||||
|
||||
public SnapshotObjectTO() {
|
||||
@ -51,6 +52,7 @@ public class SnapshotObjectTO implements DataTO {
|
||||
public SnapshotObjectTO(SnapshotInfo snapshot) {
|
||||
this.path = snapshot.getPath();
|
||||
this.setId(snapshot.getId());
|
||||
this.accountId = snapshot.getAccountId();
|
||||
VolumeInfo vol = snapshot.getBaseVolume();
|
||||
if (vol != null) {
|
||||
this.volume = (VolumeObjectTO)vol.getTO();
|
||||
@ -168,6 +170,14 @@ public class SnapshotObjectTO implements DataTO {
|
||||
return parents;
|
||||
}
|
||||
|
||||
public long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public void setAccountId(long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder("SnapshotTO[datastore=").append(dataStore).append("|volume=").append(volume).append("|path").append(path).append("]").toString();
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
// 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 com.cloud.storage.resource;
|
||||
|
||||
import org.apache.cloudstack.storage.command.QuerySnapshotZoneCopyAnswer;
|
||||
import org.apache.cloudstack.storage.command.QuerySnapshotZoneCopyCommand;
|
||||
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class StorageSubsystemCommandHandlerBaseTest {
|
||||
|
||||
@Test
|
||||
public void testHandleQuerySnapshotCommand() {
|
||||
StorageSubsystemCommandHandlerBase storageSubsystemCommandHandlerBase = new StorageSubsystemCommandHandlerBase(Mockito.mock(StorageProcessor.class));
|
||||
QuerySnapshotZoneCopyCommand querySnapshotZoneCopyCommand = new QuerySnapshotZoneCopyCommand(Mockito.mock(SnapshotObjectTO.class));
|
||||
Answer answer = storageSubsystemCommandHandlerBase.handleStorageCommands(querySnapshotZoneCopyCommand);
|
||||
Assert.assertTrue(answer instanceof QuerySnapshotZoneCopyAnswer);
|
||||
QuerySnapshotZoneCopyAnswer querySnapshotZoneCopyAnswer = (QuerySnapshotZoneCopyAnswer)answer;
|
||||
Assert.assertFalse(querySnapshotZoneCopyAnswer.getResult());
|
||||
Assert.assertEquals("Unsupported command", querySnapshotZoneCopyAnswer.getDetails());
|
||||
}
|
||||
}
|
||||
@ -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.storage.command;
|
||||
|
||||
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public class DownloadCommandTest {
|
||||
|
||||
@Test
|
||||
public void testDownloadCOmmandSnapshot() {
|
||||
SnapshotObjectTO snapshotObjectTO = Mockito.mock(SnapshotObjectTO.class);
|
||||
Long maxDownloadSizeInBytes = 1000L;
|
||||
String url = "SOMEURL";
|
||||
DownloadCommand cmd = new DownloadCommand(snapshotObjectTO, maxDownloadSizeInBytes, url);
|
||||
Assert.assertEquals(DownloadCommand.ResourceType.SNAPSHOT, cmd.getResourceType());
|
||||
Assert.assertEquals(maxDownloadSizeInBytes, cmd.getMaxDownloadSizeInBytes());
|
||||
Assert.assertEquals(url, cmd.getUrl());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
// 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.storage.command;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public class QuerySnapshotZoneCopyAnswerTest {
|
||||
|
||||
@Test
|
||||
public void testQuerySnapshotZoneCopyAnswerSuccess() {
|
||||
QuerySnapshotZoneCopyCommand cmd = Mockito.mock(QuerySnapshotZoneCopyCommand.class);
|
||||
List<String> files = List.of("File1", "File2");
|
||||
QuerySnapshotZoneCopyAnswer answer = new QuerySnapshotZoneCopyAnswer(cmd, files);
|
||||
Assert.assertTrue(answer.getResult());
|
||||
Assert.assertEquals(files.size(), answer.getFiles().size());
|
||||
Assert.assertEquals(files.get(0), answer.getFiles().get(0));
|
||||
Assert.assertEquals(files.get(1), answer.getFiles().get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuerySnapshotZoneCopyAnswerFailure() {
|
||||
QuerySnapshotZoneCopyCommand cmd = Mockito.mock(QuerySnapshotZoneCopyCommand.class);
|
||||
String err = "SOMEERROR";
|
||||
QuerySnapshotZoneCopyAnswer answer = new QuerySnapshotZoneCopyAnswer(cmd, err);
|
||||
Assert.assertFalse(answer.getResult());
|
||||
Assert.assertEquals(err, answer.getDetails());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
// 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.storage.to;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
public class SnapshotObjectTOTest {
|
||||
|
||||
@Test
|
||||
public void testAccountId() {
|
||||
SnapshotObjectTO obj = new SnapshotObjectTO();
|
||||
long accountId = 1L;
|
||||
ReflectionTestUtils.setField(obj, "accountId", accountId);
|
||||
Assert.assertEquals(accountId, obj.getAccountId());
|
||||
accountId = 100L;
|
||||
obj.setAccountId(accountId);
|
||||
Assert.assertEquals(accountId, obj.getAccountId());
|
||||
SnapshotInfo snapshot = Mockito.mock(SnapshotInfo.class);
|
||||
Mockito.when(snapshot.getAccountId()).thenReturn(accountId);
|
||||
Mockito.when(snapshot.getDataStore()).thenReturn(Mockito.mock(DataStore.class));
|
||||
SnapshotObjectTO object = new SnapshotObjectTO(snapshot);
|
||||
Assert.assertEquals(accountId, object.getAccountId());
|
||||
}
|
||||
}
|
||||
@ -54,4 +54,6 @@ public interface DataStoreManager {
|
||||
List<DataStore> listImageCacheStores();
|
||||
|
||||
boolean isRegionStore(DataStore store);
|
||||
|
||||
Long getStoreZoneId(long storeId, DataStoreRole role);
|
||||
}
|
||||
|
||||
@ -27,11 +27,17 @@ public interface SnapshotDataFactory {
|
||||
|
||||
SnapshotInfo getSnapshot(DataObject obj, DataStore store);
|
||||
|
||||
SnapshotInfo getSnapshot(long snapshotId, DataStoreRole role);
|
||||
SnapshotInfo getSnapshot(long snapshotId, long storeId, DataStoreRole role);
|
||||
|
||||
SnapshotInfo getSnapshot(long snapshotId, DataStoreRole role, boolean retrieveAnySnapshotFromVolume);
|
||||
SnapshotInfo getSnapshotWithRoleAndZone(long snapshotId, DataStoreRole role, long zoneId);
|
||||
|
||||
List<SnapshotInfo> getSnapshots(long volumeId, DataStoreRole store);
|
||||
SnapshotInfo getSnapshotOnPrimaryStore(long snapshotId);
|
||||
|
||||
SnapshotInfo getSnapshot(long snapshotId, DataStoreRole role, long zoneId, boolean retrieveAnySnapshotFromVolume);
|
||||
|
||||
List<SnapshotInfo> getSnapshotsForVolumeAndStoreRole(long volumeId, DataStoreRole store);
|
||||
|
||||
List<SnapshotInfo> getSnapshots(long snapshotId, Long zoneId);
|
||||
|
||||
List<SnapshotInfo> listSnapshotOnCache(long snapshotId);
|
||||
|
||||
|
||||
@ -57,4 +57,6 @@ public interface SnapshotInfo extends DataObject, Snapshot {
|
||||
void markBackedUp() throws CloudRuntimeException;
|
||||
|
||||
Snapshot getSnapshotVO();
|
||||
|
||||
long getAccountId();
|
||||
}
|
||||
|
||||
@ -17,6 +17,9 @@
|
||||
|
||||
package org.apache.cloudstack.engine.subsystem.api.storage;
|
||||
|
||||
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
||||
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.storage.Snapshot.Event;
|
||||
|
||||
public interface SnapshotService {
|
||||
@ -35,4 +38,8 @@ public interface SnapshotService {
|
||||
void processEventOnSnapshotObject(SnapshotInfo snapshot, Event event);
|
||||
|
||||
void cleanupOnSnapshotBackupFailure(SnapshotInfo snapshot);
|
||||
|
||||
AsyncCallFuture<SnapshotResult> copySnapshot(SnapshotInfo snapshot, String copyUrl, DataStore dataStore) throws ResourceUnavailableException;
|
||||
|
||||
AsyncCallFuture<CreateCmdResult> queryCopySnapshot(SnapshotInfo snapshot) throws ResourceUnavailableException;
|
||||
}
|
||||
|
||||
@ -28,11 +28,11 @@ public interface SnapshotStrategy {
|
||||
|
||||
SnapshotInfo backupSnapshot(SnapshotInfo snapshot);
|
||||
|
||||
boolean deleteSnapshot(Long snapshotId);
|
||||
boolean deleteSnapshot(Long snapshotId, Long zoneId);
|
||||
|
||||
boolean revertSnapshot(SnapshotInfo snapshot);
|
||||
|
||||
StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op);
|
||||
StrategyPriority canHandle(Snapshot snapshot, Long zoneId, SnapshotOperation op);
|
||||
|
||||
void postSnapshotCreation(SnapshotInfo snapshot);
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@ public interface StorageStrategyFactory {
|
||||
|
||||
SnapshotStrategy getSnapshotStrategy(Snapshot snapshot, SnapshotOperation op);
|
||||
|
||||
SnapshotStrategy getSnapshotStrategy(Snapshot snapshot, Long zoneId, SnapshotOperation op);
|
||||
|
||||
VMSnapshotStrategy getVmSnapshotStrategy(VMSnapshot vmSnapshot);
|
||||
|
||||
/**
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
// under the License.
|
||||
package com.cloud.vm;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.storage.Snapshot;
|
||||
|
||||
public class VmWorkTakeVolumeSnapshot extends VmWork {
|
||||
@ -29,8 +31,11 @@ public class VmWorkTakeVolumeSnapshot extends VmWork {
|
||||
private Snapshot.LocationType locationType;
|
||||
private boolean asyncBackup;
|
||||
|
||||
private List<Long> zoneIds;
|
||||
|
||||
public VmWorkTakeVolumeSnapshot(long userId, long accountId, long vmId, String handlerName,
|
||||
Long volumeId, Long policyId, Long snapshotId, boolean quiesceVm, Snapshot.LocationType locationType, boolean asyncBackup) {
|
||||
Long volumeId, Long policyId, Long snapshotId, boolean quiesceVm, Snapshot.LocationType locationType,
|
||||
boolean asyncBackup, List<Long> zoneIds) {
|
||||
super(userId, accountId, vmId, handlerName);
|
||||
this.volumeId = volumeId;
|
||||
this.policyId = policyId;
|
||||
@ -38,6 +43,7 @@ public class VmWorkTakeVolumeSnapshot extends VmWork {
|
||||
this.quiesceVm = quiesceVm;
|
||||
this.locationType = locationType;
|
||||
this.asyncBackup = asyncBackup;
|
||||
this.zoneIds = zoneIds;
|
||||
}
|
||||
|
||||
public Long getVolumeId() {
|
||||
@ -61,4 +67,8 @@ public class VmWorkTakeVolumeSnapshot extends VmWork {
|
||||
public boolean isAsyncBackup() {
|
||||
return asyncBackup;
|
||||
}
|
||||
|
||||
public List<Long> getZoneIds() {
|
||||
return zoneIds;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 com.cloud.vm;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class VmWorkTakeVolumeSnapshotTest {
|
||||
|
||||
@Test
|
||||
public void testVmWorkTakeVolumeSnapshotZoneIds() {
|
||||
List<Long> zoneIds = List.of(10L, 20L);
|
||||
VmWorkTakeVolumeSnapshot work = new VmWorkTakeVolumeSnapshot(1L, 1L, 1L, "handler",
|
||||
1L, 1L, 1L, false, null, false, zoneIds);
|
||||
Assert.assertNotNull(work.getZoneIds());
|
||||
Assert.assertEquals(zoneIds.size(), work.getZoneIds().size());
|
||||
Assert.assertEquals(zoneIds.get(0), work.getZoneIds().get(0));
|
||||
Assert.assertEquals(zoneIds.get(1), work.getZoneIds().get(1));
|
||||
}
|
||||
}
|
||||
@ -46,6 +46,7 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.Status;
|
||||
@ -61,7 +62,6 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.SecondaryStorageVmVO;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.dao.SecondaryStorageVmDao;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public class DataMigrationUtility {
|
||||
private static Logger LOGGER = Logger.getLogger(DataMigrationUtility.class);
|
||||
@ -223,7 +223,7 @@ public class DataMigrationUtility {
|
||||
if (snapshot.getState() == ObjectInDataStoreStateMachine.State.Ready &&
|
||||
snapshotVO != null && snapshotVO.getHypervisorType() != Hypervisor.HypervisorType.Simulator
|
||||
&& snapshot.getParentSnapshotId() == 0 ) {
|
||||
SnapshotInfo snap = snapshotFactory.getSnapshot(snapshotVO.getSnapshotId(), DataStoreRole.Image);
|
||||
SnapshotInfo snap = snapshotFactory.getSnapshot(snapshotVO.getSnapshotId(), snapshot.getDataStoreId(), snapshot.getRole());
|
||||
if (snap != null) {
|
||||
files.add(snap);
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
|
||||
package org.apache.cloudstack.engine.orchestration;
|
||||
|
||||
import com.cloud.capacity.CapacityManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@ -58,6 +57,7 @@ import org.apache.commons.math3.stat.descriptive.moment.Mean;
|
||||
import org.apache.commons.math3.stat.descriptive.moment.StandardDeviation;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.capacity.CapacityManager;
|
||||
import com.cloud.server.StatsCollector;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
@ -305,7 +305,7 @@ public class StorageOrchestrator extends ManagerBase implements StorageOrchestra
|
||||
if (!snaps.isEmpty()) {
|
||||
for (SnapshotDataStoreVO snap : snaps) {
|
||||
SnapshotVO snapshotVO = snapshotDao.findById(snap.getSnapshotId());
|
||||
SnapshotInfo snapshotInfo = snapshotFactory.getSnapshot(snapshotVO.getSnapshotId(), DataStoreRole.Image);
|
||||
SnapshotInfo snapshotInfo = snapshotFactory.getSnapshot(snapshotVO.getSnapshotId(), snap.getDataStoreId(), DataStoreRole.Image);
|
||||
SnapshotInfo parentSnapshot = snapshotInfo.getParent();
|
||||
|
||||
if (parentSnapshot == null && policy == MigrationPolicy.COMPLETE) {
|
||||
|
||||
@ -558,7 +558,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
||||
VolumeInfo vol = volFactory.getVolume(volume.getId());
|
||||
DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
|
||||
DataStoreRole dataStoreRole = snapshotHelper.getDataStoreRole(snapshot);
|
||||
SnapshotInfo snapInfo = snapshotFactory.getSnapshot(snapshot.getId(), dataStoreRole);
|
||||
SnapshotInfo snapInfo = snapshotFactory.getSnapshotWithRoleAndZone(snapshot.getId(), dataStoreRole, volume.getDataCenterId());
|
||||
|
||||
boolean kvmSnapshotOnlyInPrimaryStorage = snapshotHelper.isKvmSnapshotOnlyInPrimaryStorage(snapshot, dataStoreRole);
|
||||
|
||||
|
||||
@ -115,4 +115,6 @@ public interface DataCenterDao extends GenericDao<DataCenterVO, Long> {
|
||||
List<DataCenterVO> findByKeyword(String keyword);
|
||||
|
||||
List<DataCenterVO> listAllZones();
|
||||
|
||||
List<DataCenterVO> listByIds(List<Long> ids);
|
||||
}
|
||||
|
||||
@ -433,4 +433,14 @@ public class DataCenterDaoImpl extends GenericDaoBase<DataCenterVO, Long> implem
|
||||
|
||||
return dcs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DataCenterVO> listByIds(List<Long> ids) {
|
||||
SearchBuilder<DataCenterVO> idsSearch = createSearchBuilder();
|
||||
idsSearch.and("ids", idsSearch.entity().getId(), SearchCriteria.Op.IN);
|
||||
idsSearch.done();
|
||||
SearchCriteria<DataCenterVO> sc = idsSearch.create();
|
||||
sc.setParameters("ids", ids.toArray());
|
||||
return listBy(sc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,9 +16,8 @@
|
||||
// under the License.
|
||||
package com.cloud.storage;
|
||||
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
@ -32,8 +31,9 @@ import javax.persistence.Table;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
@Entity
|
||||
@Table(name = "snapshots")
|
||||
|
||||
@ -0,0 +1,118 @@
|
||||
// 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 com.cloud.storage;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
|
||||
@Entity
|
||||
@Table(name = "snapshot_zone_ref")
|
||||
public class SnapshotZoneVO implements InternalIdentity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
Long id;
|
||||
|
||||
@Column(name = "zone_id")
|
||||
private long zoneId;
|
||||
|
||||
@Column(name = "snapshot_id")
|
||||
private long snapshotId;
|
||||
|
||||
@Column(name = GenericDaoBase.CREATED_COLUMN)
|
||||
private Date created = null;
|
||||
|
||||
@Column(name = "last_updated")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date lastUpdated = null;
|
||||
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||
private Date removed;
|
||||
|
||||
protected SnapshotZoneVO() {
|
||||
|
||||
}
|
||||
|
||||
public SnapshotZoneVO(long zoneId, long snapshotId, Date lastUpdated) {
|
||||
this.zoneId = zoneId;
|
||||
this.snapshotId = snapshotId;
|
||||
this.lastUpdated = lastUpdated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public void setZoneId(long zoneId) {
|
||||
this.zoneId = zoneId;
|
||||
}
|
||||
|
||||
public long getSnapshotId() {
|
||||
return snapshotId;
|
||||
}
|
||||
|
||||
public void setSnapshotId(long snapshotId) {
|
||||
this.snapshotId = snapshotId;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Date getLastUpdated() {
|
||||
return lastUpdated;
|
||||
}
|
||||
|
||||
public void setLastUpdated(Date lastUpdated) {
|
||||
this.lastUpdated = lastUpdated;
|
||||
}
|
||||
|
||||
public void setRemoved(Date removed) {
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
public Date getRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
// 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 com.cloud.storage.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.storage.SnapshotZoneVO;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface SnapshotZoneDao extends GenericDao<SnapshotZoneVO, Long> {
|
||||
SnapshotZoneVO findByZoneSnapshot(long zoneId, long templateId);
|
||||
void addSnapshotToZone(long snapshotId, long zoneId);
|
||||
void removeSnapshotFromZone(long snapshotId, long zoneId);
|
||||
void removeSnapshotFromZones(long snapshotId);
|
||||
List<SnapshotZoneVO> listBySnapshot(long snapshotId);
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
// 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 com.cloud.storage.dao;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.storage.SnapshotZoneVO;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
public class SnapshotZoneDaoImpl extends GenericDaoBase<SnapshotZoneVO, Long> implements SnapshotZoneDao {
|
||||
public static final Logger s_logger = Logger.getLogger(SnapshotZoneDaoImpl.class.getName());
|
||||
protected final SearchBuilder<SnapshotZoneVO> ZoneSnapshotSearch;
|
||||
|
||||
public SnapshotZoneDaoImpl() {
|
||||
|
||||
ZoneSnapshotSearch = createSearchBuilder();
|
||||
ZoneSnapshotSearch.and("zone_id", ZoneSnapshotSearch.entity().getZoneId(), SearchCriteria.Op.EQ);
|
||||
ZoneSnapshotSearch.and("snapshot_id", ZoneSnapshotSearch.entity().getSnapshotId(), SearchCriteria.Op.EQ);
|
||||
ZoneSnapshotSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotZoneVO findByZoneSnapshot(long zoneId, long snapshotId) {
|
||||
SearchCriteria<SnapshotZoneVO> sc = ZoneSnapshotSearch.create();
|
||||
sc.setParameters("zone_id", zoneId);
|
||||
sc.setParameters("snapshot_id", snapshotId);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSnapshotToZone(long snapshotId, long zoneId) {
|
||||
SnapshotZoneVO snapshotZone = findByZoneSnapshot(zoneId, snapshotId);
|
||||
if (snapshotZone == null) {
|
||||
snapshotZone = new SnapshotZoneVO(zoneId, snapshotId, new Date());
|
||||
persist(snapshotZone);
|
||||
} else {
|
||||
snapshotZone.setRemoved(GenericDaoBase.DATE_TO_NULL);
|
||||
snapshotZone.setLastUpdated(new Date());
|
||||
update(snapshotZone.getId(), snapshotZone);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSnapshotFromZone(long snapshotId, long zoneId) {
|
||||
SearchCriteria<SnapshotZoneVO> sc = ZoneSnapshotSearch.create();
|
||||
sc.setParameters("zone_id", zoneId);
|
||||
sc.setParameters("snapshot_id", snapshotId);
|
||||
remove(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSnapshotFromZones(long snapshotId) {
|
||||
SearchCriteria<SnapshotZoneVO> sc = ZoneSnapshotSearch.create();
|
||||
sc.setParameters("snapshot_id", snapshotId);
|
||||
remove(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotZoneVO> listBySnapshot(long snapshotId) {
|
||||
SearchCriteria<SnapshotZoneVO> sc = ZoneSnapshotSearch.create();
|
||||
sc.setParameters("snapshot_id", snapshotId);
|
||||
return listBy(sc);
|
||||
}
|
||||
}
|
||||
@ -23,6 +23,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import com.cloud.utils.fsm.StateDao;
|
||||
|
||||
@ -33,15 +34,21 @@ StateDao<ObjectInDataStoreStateMachine.State, ObjectInDataStoreStateMachine.Even
|
||||
|
||||
List<SnapshotDataStoreVO> listByStoreIdAndState(long id, ObjectInDataStoreStateMachine.State state);
|
||||
|
||||
List<SnapshotDataStoreVO> listBySnapshotIdAndState(long id, ObjectInDataStoreStateMachine.State state);
|
||||
|
||||
List<SnapshotDataStoreVO> listActiveOnCache(long id);
|
||||
|
||||
void deletePrimaryRecordsForStore(long id, DataStoreRole role);
|
||||
|
||||
SnapshotDataStoreVO findByStoreSnapshot(DataStoreRole role, long storeId, long snapshotId);
|
||||
|
||||
void removeBySnapshotStore(long snapshotId, long storeId, DataStoreRole role);
|
||||
|
||||
SnapshotDataStoreVO findParent(DataStoreRole role, Long storeId, Long volumeId);
|
||||
|
||||
SnapshotDataStoreVO findBySnapshot(long snapshotId, DataStoreRole role);
|
||||
List<SnapshotDataStoreVO> listBySnapshot(long snapshotId, DataStoreRole role);
|
||||
|
||||
List<SnapshotDataStoreVO> listReadyBySnapshot(long snapshotId, DataStoreRole role);
|
||||
|
||||
SnapshotDataStoreVO findBySourceSnapshot(long snapshotId, DataStoreRole role);
|
||||
|
||||
@ -66,9 +73,7 @@ StateDao<ObjectInDataStoreStateMachine.State, ObjectInDataStoreStateMachine.Even
|
||||
|
||||
void updateVolumeIds(long oldVolId, long newVolId);
|
||||
|
||||
SnapshotDataStoreVO findByVolume(long volumeId, DataStoreRole role);
|
||||
|
||||
SnapshotDataStoreVO findByVolume(long snapshotId, long volumeId, DataStoreRole role);
|
||||
List<SnapshotDataStoreVO> findByVolume(long snapshotId, long volumeId, DataStoreRole role);
|
||||
|
||||
/**
|
||||
* List all snapshots in 'snapshot_store_ref' by volume and data store role. Therefore, it is possible to list all snapshots that are in the primary storage or in the secondary storage.
|
||||
@ -85,10 +90,16 @@ StateDao<ObjectInDataStoreStateMachine.State, ObjectInDataStoreStateMachine.Even
|
||||
* Removes the snapshot reference from the database according to its id and data store role.
|
||||
* @return true if success, otherwise, false.
|
||||
*/
|
||||
boolean expungeReferenceBySnapshotIdAndDataStoreRole(long snapshotId, DataStoreRole dataStorerole);
|
||||
boolean expungeReferenceBySnapshotIdAndDataStoreRole(long snapshotId, long storeId, DataStoreRole dataStorerole);
|
||||
|
||||
/**
|
||||
* List all snapshots in 'snapshot_store_ref' with state 'Ready' by volume ID.
|
||||
*/
|
||||
List<SnapshotDataStoreVO> listReadyByVolumeId(long volumeId);
|
||||
|
||||
List<SnapshotDataStoreVO> listBySnasphotStoreDownloadStatus(long snapshotId, long storeId, VMTemplateStorageResourceAssoc.Status... status);
|
||||
|
||||
SnapshotDataStoreVO findOneBySnapshotAndDatastoreRole(long snapshotId, DataStoreRole role);
|
||||
|
||||
void updateDisplayForSnapshotStoreRole(long snapshotId, long storeId, DataStoreRole role, boolean display);
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@ import org.springframework.stereotype.Component;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.db.Filter;
|
||||
@ -61,8 +62,10 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
||||
private SearchBuilder<SnapshotDataStoreVO> searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq;
|
||||
protected SearchBuilder<SnapshotDataStoreVO> searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq;
|
||||
private SearchBuilder<SnapshotDataStoreVO> stateSearch;
|
||||
private SearchBuilder<SnapshotDataStoreVO> idStateNeqSearch;
|
||||
protected SearchBuilder<SnapshotVO> snapshotVOSearch;
|
||||
private SearchBuilder<SnapshotDataStoreVO> snapshotCreatedSearch;
|
||||
private SearchBuilder<SnapshotDataStoreVO> storeSnapshotDownloadStatusSearch;
|
||||
|
||||
protected static final List<Hypervisor.HypervisorType> HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING = List.of(Hypervisor.HypervisorType.XenServer);
|
||||
|
||||
@ -114,6 +117,12 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
||||
stateSearch.and(STATE, stateSearch.entity().getState(), SearchCriteria.Op.IN);
|
||||
stateSearch.done();
|
||||
|
||||
|
||||
idStateNeqSearch = createSearchBuilder();
|
||||
idStateNeqSearch.and(SNAPSHOT_ID, idStateNeqSearch.entity().getSnapshotId(), SearchCriteria.Op.EQ);
|
||||
idStateNeqSearch.and(STATE, idStateNeqSearch.entity().getState(), SearchCriteria.Op.NEQ);
|
||||
idStateNeqSearch.done();
|
||||
|
||||
snapshotVOSearch = snapshotDao.createSearchBuilder();
|
||||
snapshotVOSearch.and(VOLUME_ID, snapshotVOSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
|
||||
snapshotVOSearch.done();
|
||||
@ -123,6 +132,12 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
||||
snapshotCreatedSearch.and(CREATED, snapshotCreatedSearch.entity().getCreated(), SearchCriteria.Op.BETWEEN);
|
||||
snapshotCreatedSearch.done();
|
||||
|
||||
storeSnapshotDownloadStatusSearch = createSearchBuilder();
|
||||
storeSnapshotDownloadStatusSearch.and(SNAPSHOT_ID, storeSnapshotDownloadStatusSearch.entity().getSnapshotId(), SearchCriteria.Op.EQ);
|
||||
storeSnapshotDownloadStatusSearch.and(STORE_ID, storeSnapshotDownloadStatusSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ);
|
||||
storeSnapshotDownloadStatusSearch.and("downloadState", storeSnapshotDownloadStatusSearch.entity().getDownloadState(), SearchCriteria.Op.IN);
|
||||
storeSnapshotDownloadStatusSearch.done();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -179,6 +194,14 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotDataStoreVO> listBySnapshotIdAndState(long id, ObjectInDataStoreStateMachine.State state) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create();
|
||||
sc.setParameters(SNAPSHOT_ID, id);
|
||||
sc.setParameters(STATE, state);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletePrimaryRecordsForStore(long id, DataStoreRole role) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq.create();
|
||||
@ -203,6 +226,15 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBySnapshotStore(long snapshotId, long storeId, DataStoreRole role) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create();
|
||||
sc.setParameters(STORE_ID, storeId);
|
||||
sc.setParameters(SNAPSHOT_ID, snapshotId);
|
||||
sc.setParameters(STORE_ROLE, role);
|
||||
remove(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotDataStoreVO findLatestSnapshotForVolume(Long volumeId, DataStoreRole role) {
|
||||
return findOldestOrLatestSnapshotForVolume(volumeId, role, false);
|
||||
@ -257,10 +289,16 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotDataStoreVO findBySnapshot(long snapshotId, DataStoreRole role) {
|
||||
public List<SnapshotDataStoreVO> listBySnapshot(long snapshotId, DataStoreRole role) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotDataStoreVO> listReadyBySnapshot(long snapshotId, DataStoreRole role) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role);
|
||||
sc.setParameters(STATE, State.Ready);
|
||||
return findOneBy(sc);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -279,26 +317,19 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotDataStoreVO findByVolume(long volumeId, DataStoreRole role) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create();
|
||||
sc.setParameters(VOLUME_ID, volumeId);
|
||||
sc.setParameters(STORE_ROLE, role);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotDataStoreVO findByVolume(long snapshotId, long volumeId, DataStoreRole role) {
|
||||
public List<SnapshotDataStoreVO> findByVolume(long snapshotId, long volumeId, DataStoreRole role) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create();
|
||||
sc.setParameters(SNAPSHOT_ID, snapshotId);
|
||||
sc.setParameters(VOLUME_ID, volumeId);
|
||||
sc.setParameters(STORE_ROLE, role);
|
||||
return findOneBy(sc);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotDataStoreVO> findBySnapshotId(long snapshotId) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create();
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = idStateNeqSearch.create();
|
||||
sc.setParameters(SNAPSHOT_ID, snapshotId);
|
||||
sc.setParameters(STATE, State.Destroyed);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@ -451,8 +482,8 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean expungeReferenceBySnapshotIdAndDataStoreRole(long snapshotId, DataStoreRole dataStoreRole) {
|
||||
SnapshotDataStoreVO snapshotDataStoreVo = findOneBy(createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, dataStoreRole));
|
||||
public boolean expungeReferenceBySnapshotIdAndDataStoreRole(long snapshotId, long storeId, DataStoreRole dataStoreRole) {
|
||||
SnapshotDataStoreVO snapshotDataStoreVo = findByStoreSnapshot(dataStoreRole, storeId, snapshotId);
|
||||
return snapshotDataStoreVo == null || expunge(snapshotDataStoreVo.getId());
|
||||
}
|
||||
|
||||
@ -463,4 +494,30 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
|
||||
sc.setParameters(STATE, State.Ready);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotDataStoreVO> listBySnasphotStoreDownloadStatus(long snapshotId, long storeId, VMTemplateStorageResourceAssoc.Status... status) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = storeSnapshotDownloadStatusSearch.create();
|
||||
sc.setParameters("snapshot_id", snapshotId);
|
||||
sc.setParameters("store_id", storeId);
|
||||
sc.setParameters("downloadState", (Object[])status);
|
||||
return search(sc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotDataStoreVO findOneBySnapshotAndDatastoreRole(long snapshotId, DataStoreRole role) {
|
||||
SearchCriteria<SnapshotDataStoreVO> sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role);
|
||||
sc.setParameters(STATE, State.Ready);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDisplayForSnapshotStoreRole(long snapshotId, long storeId, DataStoreRole role, boolean display) {
|
||||
SnapshotDataStoreVO ref = findByStoreSnapshot(role, storeId, snapshotId);
|
||||
if (ref == null) {
|
||||
return;
|
||||
}
|
||||
ref.setDisplay(display);
|
||||
update(ref.getId(), ref);
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreState
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State;
|
||||
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.fsm.StateObject;
|
||||
|
||||
@ -95,6 +96,22 @@ public class SnapshotDataStoreVO implements StateObject<ObjectInDataStoreStateMa
|
||||
@Enumerated(EnumType.STRING)
|
||||
ObjectInDataStoreStateMachine.State state;
|
||||
|
||||
@Column(name = "download_pct")
|
||||
private int downloadPercent;
|
||||
|
||||
@Column(name = "download_state")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private VMTemplateStorageResourceAssoc.Status downloadState;
|
||||
|
||||
@Column(name = "local_path")
|
||||
private String localDownloadPath;
|
||||
|
||||
@Column(name = "error_str")
|
||||
private String errorString;
|
||||
|
||||
@Column(name = "display")
|
||||
private boolean display = true;
|
||||
|
||||
@Column(name = "ref_cnt")
|
||||
Long refCnt = 0L;
|
||||
|
||||
@ -295,4 +312,44 @@ public class SnapshotDataStoreVO implements StateObject<ObjectInDataStoreStateMa
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public int getDownloadPercent() {
|
||||
return downloadPercent;
|
||||
}
|
||||
|
||||
public void setDownloadPercent(int downloadPercent) {
|
||||
this.downloadPercent = downloadPercent;
|
||||
}
|
||||
|
||||
public VMTemplateStorageResourceAssoc.Status getDownloadState() {
|
||||
return downloadState;
|
||||
}
|
||||
|
||||
public void setDownloadState(VMTemplateStorageResourceAssoc.Status downloadState) {
|
||||
this.downloadState = downloadState;
|
||||
}
|
||||
|
||||
public void setLocalDownloadPath(String localPath) {
|
||||
localDownloadPath = localPath;
|
||||
}
|
||||
|
||||
public String getLocalDownloadPath() {
|
||||
return localDownloadPath;
|
||||
}
|
||||
|
||||
public void setErrorString(String errorString) {
|
||||
this.errorString = errorString;
|
||||
}
|
||||
|
||||
public String getErrorString() {
|
||||
return errorString;
|
||||
}
|
||||
|
||||
public boolean isDisplay() {
|
||||
return display;
|
||||
}
|
||||
|
||||
public void setDisplay(boolean display) {
|
||||
this.display = display;
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +56,7 @@
|
||||
<bean id="serviceOfferingDaoImpl" class="com.cloud.service.dao.ServiceOfferingDaoImpl" />
|
||||
<bean id="serviceOfferingDetailsDaoImpl" class="com.cloud.service.dao.ServiceOfferingDetailsDaoImpl"/>
|
||||
<bean id="snapshotDaoImpl" class="com.cloud.storage.dao.SnapshotDaoImpl" />
|
||||
<bean id="snapshotZoneDaoImpl" class="com.cloud.storage.dao.SnapshotZoneDaoImpl" />
|
||||
<bean id="snapshotDataStoreDaoImpl" class="org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDaoImpl" />
|
||||
<bean id="storagePoolDetailsDaoImpl" class="com.cloud.storage.dao.StoragePoolDetailsDaoImpl" />
|
||||
<bean id="storagePoolHostDaoImpl" class="com.cloud.storage.dao.StoragePoolHostDaoImpl" />
|
||||
|
||||
@ -176,6 +176,7 @@
|
||||
<bean id="site2SiteCustomerGatewayDaoImpl" class="com.cloud.network.dao.Site2SiteCustomerGatewayDaoImpl" />
|
||||
<bean id="site2SiteVpnConnectionDaoImpl" class="com.cloud.network.dao.Site2SiteVpnConnectionDaoImpl" />
|
||||
<bean id="site2SiteVpnGatewayDaoImpl" class="com.cloud.network.dao.Site2SiteVpnGatewayDaoImpl" />
|
||||
<bean id="snapshotJoinDaoImpl" class="com.cloud.api.query.dao.SnapshotJoinDaoImpl" />
|
||||
<bean id="snapshotDetailsDaoImpl" class="com.cloud.storage.dao.SnapshotDetailsDaoImpl" />
|
||||
<bean id="snapshotPolicyDaoImpl" class="com.cloud.storage.dao.SnapshotPolicyDaoImpl" />
|
||||
<bean id="snapshotPolicyDetailsDaoImpl" class="org.apache.cloudstack.resourcedetail.dao.SnapshotPolicyDetailsDaoImpl" />
|
||||
|
||||
@ -183,3 +183,116 @@ ALTER TABLE `cloud`.`kubernetes_cluster` MODIFY COLUMN `kubernetes_version_id` b
|
||||
|
||||
-- Set removed state for all removed accounts
|
||||
UPDATE `cloud`.`account` SET state='removed' WHERE `removed` IS NOT NULL;
|
||||
|
||||
-- Add table for snapshot zone reference
|
||||
CREATE TABLE `cloud`.`snapshot_zone_ref` (
|
||||
`id` bigint unsigned NOT NULL auto_increment,
|
||||
`zone_id` bigint unsigned NOT NULL,
|
||||
`snapshot_id` bigint unsigned NOT NULL,
|
||||
`created` DATETIME NOT NULL,
|
||||
`last_updated` DATETIME,
|
||||
`removed` datetime COMMENT 'date removed if not null',
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `fk_snapshot_zone_ref__zone_id` FOREIGN KEY `fk_snapshot_zone_ref__zone_id` (`zone_id`) REFERENCES `data_center` (`id`) ON DELETE CASCADE,
|
||||
INDEX `i_snapshot_zone_ref__zone_id`(`zone_id`),
|
||||
CONSTRAINT `fk_snapshot_zone_ref__snapshot_id` FOREIGN KEY `fk_snapshot_zone_ref__snapshot_id` (`snapshot_id`) REFERENCES `snapshots` (`id`) ON DELETE CASCADE,
|
||||
INDEX `i_snapshot_zone_ref__snapshot_id`(`snapshot_id`),
|
||||
INDEX `i_snapshot_zone_ref__removed`(`removed`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Alter snapshot_store_ref table to add download related fields
|
||||
ALTER TABLE `cloud`.`snapshot_store_ref`
|
||||
ADD COLUMN `download_state` varchar(255) DEFAULT NULL COMMENT 'the state of the snapshot download' AFTER `volume_id`,
|
||||
ADD COLUMN `download_pct` int unsigned DEFAULT NULL COMMENT 'the percentage of the snapshot download completed' AFTER `download_state`,
|
||||
ADD COLUMN `error_str` varchar(255) DEFAULT NULL COMMENT 'the error message when the snapshot download occurs' AFTER `download_pct`,
|
||||
ADD COLUMN `local_path` varchar(255) DEFAULT NULL COMMENT 'the path of the snapshot download' AFTER `error_str`,
|
||||
ADD COLUMN `display` tinyint(1) unsigned NOT NULL DEFAULT 1 COMMENT '1 implies store reference is available for listing' AFTER `error_str`;
|
||||
|
||||
-- Create snapshot_view
|
||||
DROP VIEW IF EXISTS `cloud`.`snapshot_view`;
|
||||
CREATE VIEW `cloud`.`snapshot_view` AS
|
||||
SELECT
|
||||
`snapshots`.`id` AS `id`,
|
||||
`snapshots`.`uuid` AS `uuid`,
|
||||
`snapshots`.`name` AS `name`,
|
||||
`snapshots`.`status` AS `status`,
|
||||
`snapshots`.`disk_offering_id` AS `disk_offering_id`,
|
||||
`snapshots`.`snapshot_type` AS `snapshot_type`,
|
||||
`snapshots`.`type_description` AS `type_description`,
|
||||
`snapshots`.`size` AS `size`,
|
||||
`snapshots`.`created` AS `created`,
|
||||
`snapshots`.`removed` AS `removed`,
|
||||
`snapshots`.`location_type` AS `location_type`,
|
||||
`snapshots`.`hypervisor_type` AS `hypervisor_type`,
|
||||
`account`.`id` AS `account_id`,
|
||||
`account`.`uuid` AS `account_uuid`,
|
||||
`account`.`account_name` AS `account_name`,
|
||||
`account`.`type` AS `account_type`,
|
||||
`domain`.`id` AS `domain_id`,
|
||||
`domain`.`uuid` AS `domain_uuid`,
|
||||
`domain`.`name` AS `domain_name`,
|
||||
`domain`.`path` AS `domain_path`,
|
||||
`projects`.`id` AS `project_id`,
|
||||
`projects`.`uuid` AS `project_uuid`,
|
||||
`projects`.`name` AS `project_name`,
|
||||
`volumes`.`id` AS `volume_id`,
|
||||
`volumes`.`uuid` AS `volume_uuid`,
|
||||
`volumes`.`name` AS `volume_name`,
|
||||
`volumes`.`volume_type` AS `volume_type`,
|
||||
`volumes`.`size` AS `volume_size`,
|
||||
`data_center`.`id` AS `data_center_id`,
|
||||
`data_center`.`uuid` AS `data_center_uuid`,
|
||||
`data_center`.`name` AS `data_center_name`,
|
||||
`snapshot_store_ref`.`store_id` AS `store_id`,
|
||||
IFNULL(`image_store`.`uuid`, `storage_pool`.`uuid`) AS `store_uuid`,
|
||||
IFNULL(`image_store`.`name`, `storage_pool`.`name`) AS `store_name`,
|
||||
`snapshot_store_ref`.`store_role` AS `store_role`,
|
||||
`snapshot_store_ref`.`state` AS `store_state`,
|
||||
`snapshot_store_ref`.`download_state` AS `download_state`,
|
||||
`snapshot_store_ref`.`download_pct` AS `download_pct`,
|
||||
`snapshot_store_ref`.`error_str` AS `error_str`,
|
||||
`snapshot_store_ref`.`size` AS `store_size`,
|
||||
`snapshot_store_ref`.`created` AS `created_on_store`,
|
||||
`resource_tags`.`id` AS `tag_id`,
|
||||
`resource_tags`.`uuid` AS `tag_uuid`,
|
||||
`resource_tags`.`key` AS `tag_key`,
|
||||
`resource_tags`.`value` AS `tag_value`,
|
||||
`resource_tags`.`domain_id` AS `tag_domain_id`,
|
||||
`domain`.`uuid` AS `tag_domain_uuid`,
|
||||
`domain`.`name` AS `tag_domain_name`,
|
||||
`resource_tags`.`account_id` AS `tag_account_id`,
|
||||
`account`.`account_name` AS `tag_account_name`,
|
||||
`resource_tags`.`resource_id` AS `tag_resource_id`,
|
||||
`resource_tags`.`resource_uuid` AS `tag_resource_uuid`,
|
||||
`resource_tags`.`resource_type` AS `tag_resource_type`,
|
||||
`resource_tags`.`customer` AS `tag_customer`,
|
||||
CONCAT(`snapshots`.`id`,
|
||||
'_',
|
||||
IFNULL(`snapshot_store_ref`.`store_role`, 'UNKNOWN'),
|
||||
'_',
|
||||
IFNULL(`snapshot_store_ref`.`store_id`, 0)) AS `snapshot_store_pair`
|
||||
FROM
|
||||
((((((((((`snapshots`
|
||||
JOIN `account` ON ((`account`.`id` = `snapshots`.`account_id`)))
|
||||
JOIN `domain` ON ((`domain`.`id` = `account`.`domain_id`)))
|
||||
LEFT JOIN `projects` ON ((`projects`.`project_account_id` = `account`.`id`)))
|
||||
LEFT JOIN `volumes` ON ((`volumes`.`id` = `snapshots`.`volume_id`)))
|
||||
LEFT JOIN `snapshot_store_ref` ON (((`snapshot_store_ref`.`snapshot_id` = `snapshots`.`id`)
|
||||
AND (`snapshot_store_ref`.`state` != 'Destroyed')
|
||||
AND (`snapshot_store_ref`.`display` = 1))))
|
||||
LEFT JOIN `image_store` ON ((ISNULL(`image_store`.`removed`)
|
||||
AND (`snapshot_store_ref`.`store_role` = 'Image')
|
||||
AND (`snapshot_store_ref`.`store_id` IS NOT NULL)
|
||||
AND (`image_store`.`id` = `snapshot_store_ref`.`store_id`))))
|
||||
LEFT JOIN `storage_pool` ON ((ISNULL(`storage_pool`.`removed`)
|
||||
AND (`snapshot_store_ref`.`store_role` = 'Primary')
|
||||
AND (`snapshot_store_ref`.`store_id` IS NOT NULL)
|
||||
AND (`storage_pool`.`id` = `snapshot_store_ref`.`store_id`))))
|
||||
LEFT JOIN `snapshot_zone_ref` ON (((`snapshot_zone_ref`.`snapshot_id` = `snapshots`.`id`)
|
||||
AND ISNULL(`snapshot_store_ref`.`store_id`)
|
||||
AND ISNULL(`snapshot_zone_ref`.`removed`))))
|
||||
LEFT JOIN `data_center` ON (((`image_store`.`data_center_id` = `data_center`.`id`)
|
||||
OR (`storage_pool`.`data_center_id` = `data_center`.`id`)
|
||||
OR (`snapshot_zone_ref`.`zone_id` = `data_center`.`id`))))
|
||||
LEFT JOIN `resource_tags` ON ((`resource_tags`.`resource_id` = `snapshots`.`id`)
|
||||
AND (`resource_tags`.`resource_type` = 'Snapshot')));
|
||||
|
||||
@ -218,7 +218,7 @@ public class SecondaryStorageServiceImpl implements SecondaryStorageService {
|
||||
private void updateDataObject(DataObject srcData, DataObject destData) {
|
||||
if (destData instanceof SnapshotInfo) {
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySourceSnapshot(srcData.getId(), DataStoreRole.Image);
|
||||
SnapshotDataStoreVO destSnapshotStore = snapshotStoreDao.findBySnapshot(srcData.getId(), DataStoreRole.Image);
|
||||
SnapshotDataStoreVO destSnapshotStore = snapshotStoreDao.findByStoreSnapshot(DataStoreRole.Image, srcData.getDataStore().getId(), srcData.getId());
|
||||
if (snapshotStore != null && destSnapshotStore != null) {
|
||||
destSnapshotStore.setPhysicalSize(snapshotStore.getPhysicalSize());
|
||||
destSnapshotStore.setCreated(snapshotStore.getCreated());
|
||||
|
||||
@ -257,4 +257,10 @@ public class ImageStoreProviderManagerImpl implements ImageStoreProviderManager,
|
||||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey<?>[] { ImageStoreAllocationAlgorithm };
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getImageStoreZoneId(long dataStoreId) {
|
||||
ImageStoreVO dataStore = dataStoreDao.findById(dataStoreId);
|
||||
return dataStore.getDataCenterId();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
// 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.storage.image.manager;
|
||||
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ImageStoreProviderManagerImplTest {
|
||||
|
||||
@Mock
|
||||
ImageStoreDao imageStoreDao;
|
||||
|
||||
@InjectMocks
|
||||
ImageStoreProviderManagerImpl imageStoreProviderManager = new ImageStoreProviderManagerImpl();
|
||||
@Test
|
||||
public void testGetImageStoreZoneId() {
|
||||
final long storeId = 1L;
|
||||
final long zoneId = 1L;
|
||||
ImageStoreVO imageStoreVO = Mockito.mock(ImageStoreVO.class);
|
||||
Mockito.when(imageStoreVO.getDataCenterId()).thenReturn(zoneId);
|
||||
Mockito.when(imageStoreDao.findById(storeId)).thenReturn(imageStoreVO);
|
||||
long value = imageStoreProviderManager.getImageStoreZoneId(storeId);
|
||||
Assert.assertEquals(zoneId, value);
|
||||
}
|
||||
}
|
||||
@ -21,7 +21,6 @@ package org.apache.cloudstack.storage.snapshot;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy.SnapshotOperation;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
@ -48,7 +47,7 @@ public class CephSnapshotStrategy extends StorageSystemSnapshotStrategy {
|
||||
private static final Logger s_logger = Logger.getLogger(CephSnapshotStrategy.class);
|
||||
|
||||
@Override
|
||||
public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) {
|
||||
public StrategyPriority canHandle(Snapshot snapshot, Long zoneId, SnapshotOperation op) {
|
||||
long volumeId = snapshot.getVolumeId();
|
||||
VolumeVO volumeVO = volumeDao.findByIdIncludingRemoved(volumeId);
|
||||
boolean baseVolumeExists = volumeVO.getRemoved() == null;
|
||||
@ -56,7 +55,7 @@ public class CephSnapshotStrategy extends StorageSystemSnapshotStrategy {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
|
||||
if (!isSnapshotStoredOnRbdStoragePool(snapshot)) {
|
||||
if (!isSnapshotStoredOnRbdStoragePoolAndOperationForSameZone(snapshot, zoneId)) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
|
||||
@ -81,12 +80,18 @@ public class CephSnapshotStrategy extends StorageSystemSnapshotStrategy {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isSnapshotStoredOnRbdStoragePool(Snapshot snapshot) {
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
protected boolean isSnapshotStoredOnRbdStoragePoolAndOperationForSameZone(Snapshot snapshot, Long zoneId) {
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshot.getId(), DataStoreRole.Primary);
|
||||
if (snapshotStore == null) {
|
||||
return false;
|
||||
}
|
||||
StoragePoolVO storagePoolVO = primaryDataStoreDao.findById(snapshotStore.getDataStoreId());
|
||||
return storagePoolVO != null && storagePoolVO.getPoolType() == StoragePoolType.RBD;
|
||||
if (storagePoolVO == null) {
|
||||
return false;
|
||||
}
|
||||
if (zoneId != null && !zoneId.equals(storagePoolVO.getDataCenterId())) {
|
||||
return false;
|
||||
}
|
||||
return storagePoolVO.getPoolType() == StoragePoolType.RBD;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,18 +16,13 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.storage.snapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.storage.VolumeDetailVO;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
|
||||
@ -41,11 +36,15 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJob;
|
||||
import org.apache.cloudstack.storage.command.CreateObjectAnswer;
|
||||
import org.apache.cloudstack.storage.datastore.PrimaryDataStoreImpl;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.PrimaryDataStoreImpl;
|
||||
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.to.DataTO;
|
||||
import com.cloud.event.EventTypes;
|
||||
@ -57,28 +56,28 @@ import com.cloud.storage.CreateSnapshotPayload;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
import com.cloud.storage.Storage.StoragePoolType;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.StoragePoolStatus;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.storage.VolumeDetailVO;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.storage.dao.SnapshotDetailsDao;
|
||||
import com.cloud.storage.dao.SnapshotZoneDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.storage.dao.VolumeDetailsDao;
|
||||
import com.cloud.storage.snapshot.SnapshotManager;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallbackNoReturn;
|
||||
import com.cloud.utils.db.TransactionStatus;
|
||||
import com.cloud.storage.snapshot.SnapshotManager;
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
import com.cloud.storage.Storage.StoragePoolType;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
|
||||
public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
private static final String SECONDARY_STORAGE_SNAPSHOT_ENTRY_IDENTIFIER = "secondary storage";
|
||||
private static final String PRIMARY_STORAGE_SNAPSHOT_ENTRY_IDENTIFIER = "primary storage";
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(DefaultSnapshotStrategy.class);
|
||||
|
||||
@ -100,6 +99,18 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
private SnapshotDetailsDao _snapshotDetailsDao;
|
||||
@Inject
|
||||
VolumeDetailsDao _volumeDetailsDaoImpl;
|
||||
@Inject
|
||||
SnapshotZoneDao snapshotZoneDao;
|
||||
|
||||
public SnapshotDataStoreVO getSnapshotImageStoreRef(long snapshotId, long zoneId) {
|
||||
List<SnapshotDataStoreVO> snaps = snapshotStoreDao.listReadyBySnapshot(snapshotId, DataStoreRole.Image);
|
||||
for (SnapshotDataStoreVO ref : snaps) {
|
||||
if (zoneId == dataStoreMgr.getStoreZoneId(ref.getDataStoreId(), ref.getRole())) {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotInfo backupSnapshot(SnapshotInfo snapshot) {
|
||||
@ -107,7 +118,8 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
|
||||
if (parentSnapshot != null && snapshot.getPath().equalsIgnoreCase(parentSnapshot.getPath())) {
|
||||
// don't need to backup this snapshot
|
||||
SnapshotDataStoreVO parentSnapshotOnBackupStore = snapshotStoreDao.findBySnapshot(parentSnapshot.getId(), DataStoreRole.Image);
|
||||
SnapshotDataStoreVO parentSnapshotOnBackupStore = getSnapshotImageStoreRef(parentSnapshot.getId(),
|
||||
dataStoreMgr.getStoreZoneId(parentSnapshot.getDataStore().getId(), parentSnapshot.getDataStore().getRole()));
|
||||
if (parentSnapshotOnBackupStore != null && parentSnapshotOnBackupStore.getState() == State.Ready) {
|
||||
DataStore store = dataStoreMgr.getDataStore(parentSnapshotOnBackupStore.getDataStoreId(), parentSnapshotOnBackupStore.getRole());
|
||||
|
||||
@ -159,7 +171,7 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
if (prevBackupId == 0) {
|
||||
break;
|
||||
}
|
||||
parentSnapshotOnBackupStore = snapshotStoreDao.findBySnapshot(prevBackupId, DataStoreRole.Image);
|
||||
parentSnapshotOnBackupStore = getSnapshotImageStoreRef(prevBackupId, volume.getDataCenterId());
|
||||
if (parentSnapshotOnBackupStore == null) {
|
||||
break;
|
||||
}
|
||||
@ -181,20 +193,19 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
return snapshotSvr.backupSnapshot(snapshot);
|
||||
}
|
||||
|
||||
private final List<Snapshot.State> snapshotStatesAbleToDeleteSnapshot = Arrays.asList(Snapshot.State.Destroying, Snapshot.State.Destroyed, Snapshot.State.Error);
|
||||
|
||||
protected boolean deleteSnapshotChain(SnapshotInfo snapshot, String storage) {
|
||||
protected boolean deleteSnapshotChain(SnapshotInfo snapshot, String storageToString) {
|
||||
DataTO snapshotTo = snapshot.getTO();
|
||||
s_logger.debug(String.format("Deleting %s chain of snapshots.", snapshotTo));
|
||||
|
||||
boolean result = false;
|
||||
boolean resultIsSet = false;
|
||||
final List<Snapshot.State> snapshotStatesAbleToDeleteSnapshot = Arrays.asList(Snapshot.State.BackedUp, Snapshot.State.Destroying, Snapshot.State.Destroyed, Snapshot.State.Error);
|
||||
try {
|
||||
while (snapshot != null && snapshotStatesAbleToDeleteSnapshot.contains(snapshot.getState())) {
|
||||
SnapshotInfo child = snapshot.getChild();
|
||||
|
||||
if (child != null) {
|
||||
s_logger.debug(String.format("Snapshot [%s] has child [%s], not deleting it on the storage [%s]", snapshotTo, child.getTO(), storage));
|
||||
s_logger.debug(String.format("Snapshot [%s] has child [%s], not deleting it on the storage [%s]", snapshotTo, child.getTO(), storageToString));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -207,8 +218,6 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
//NOTE: if both snapshots share the same path, it's for xenserver's empty delta snapshot. We can't delete the snapshot on the backend, as parent snapshot still reference to it
|
||||
//Instead, mark it as destroyed in the db.
|
||||
s_logger.debug(String.format("Snapshot [%s] is an empty delta snapshot; therefore, we will only mark it as destroyed in the database.", snapshotTo));
|
||||
snapshot.processEvent(Event.DestroyRequested);
|
||||
snapshot.processEvent(Event.OperationSuccessed);
|
||||
deleted = true;
|
||||
if (!resultIsSet) {
|
||||
result = true;
|
||||
@ -233,22 +242,25 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
resultIsSet = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s_logger.error(String.format("Failed to delete snapshot [%s] on storage [%s] due to [%s].", snapshotTo, storage, e.getMessage()), e);
|
||||
s_logger.error(String.format("Failed to delete snapshot [%s] on storage [%s] due to [%s].", snapshotTo, storageToString, e.getMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
snapshot = parent;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s_logger.error(String.format("Failed to delete snapshot [%s] on storage [%s] due to [%s].", snapshotTo, storage, e.getMessage()), e);
|
||||
s_logger.error(String.format("Failed to delete snapshot [%s] on storage [%s] due to [%s].", snapshotTo, storageToString, e.getMessage()), e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSnapshot(Long snapshotId) {
|
||||
public boolean deleteSnapshot(Long snapshotId, Long zoneId) {
|
||||
SnapshotVO snapshotVO = snapshotDao.findById(snapshotId);
|
||||
|
||||
if (zoneId != null && List.of(Snapshot.State.Allocated, Snapshot.State.CreatedOnPrimary).contains(snapshotVO.getState())) {
|
||||
throw new InvalidParameterValueException(String.format("Snapshot in %s can not be deleted for a zone", snapshotVO.getState()));
|
||||
}
|
||||
if (snapshotVO.getState() == Snapshot.State.Allocated) {
|
||||
snapshotDao.remove(snapshotId);
|
||||
return true;
|
||||
@ -260,10 +272,21 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
|
||||
if (Snapshot.State.Error.equals(snapshotVO.getState())) {
|
||||
List<SnapshotDataStoreVO> storeRefs = snapshotStoreDao.findBySnapshotId(snapshotId);
|
||||
List<Long> deletedRefs = new ArrayList<>();
|
||||
for (SnapshotDataStoreVO ref : storeRefs) {
|
||||
snapshotStoreDao.expunge(ref.getId());
|
||||
boolean refZoneIdMatch = false;
|
||||
if (zoneId != null) {
|
||||
Long refZoneId = dataStoreMgr.getStoreZoneId(ref.getDataStoreId(), ref.getRole());
|
||||
refZoneIdMatch = zoneId.equals(refZoneId);
|
||||
}
|
||||
if (zoneId == null || refZoneIdMatch) {
|
||||
snapshotStoreDao.expunge(ref.getId());
|
||||
deletedRefs.add(ref.getId());
|
||||
}
|
||||
}
|
||||
if (deletedRefs.size() == storeRefs.size()) {
|
||||
snapshotDao.remove(snapshotId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -278,20 +301,26 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
throw new InvalidParameterValueException("Can't delete snapshotshot " + snapshotId + " due to it is in " + snapshotVO.getState() + " Status");
|
||||
}
|
||||
|
||||
return destroySnapshotEntriesAndFiles(snapshotVO);
|
||||
return destroySnapshotEntriesAndFiles(snapshotVO, zoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the snapshot entries and files on both primary and secondary storage (if it exists).
|
||||
* @return true if destroy successfully, else false.
|
||||
*/
|
||||
protected boolean destroySnapshotEntriesAndFiles(SnapshotVO snapshotVo) {
|
||||
if (!deleteSnapshotInfos(snapshotVo)) {
|
||||
protected boolean destroySnapshotEntriesAndFiles(SnapshotVO snapshotVo, Long zoneId) {
|
||||
if (!deleteSnapshotInfos(snapshotVo, zoneId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (zoneId != null) {
|
||||
snapshotZoneDao.removeSnapshotFromZone(snapshotVo.getId(), zoneId);
|
||||
} else {
|
||||
snapshotZoneDao.removeSnapshotFromZones(snapshotVo.getId());
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(retrieveSnapshotEntries(snapshotVo.getId(), null))) {
|
||||
return true;
|
||||
}
|
||||
updateSnapshotToDestroyed(snapshotVo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -303,12 +332,12 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
snapshotDao.update(snapshotVo.getId(), snapshotVo);
|
||||
}
|
||||
|
||||
protected boolean deleteSnapshotInfos(SnapshotVO snapshotVo) {
|
||||
Map<String, SnapshotInfo> snapshotInfos = retrieveSnapshotEntries(snapshotVo.getId());
|
||||
protected boolean deleteSnapshotInfos(SnapshotVO snapshotVo, Long zoneId) {
|
||||
List<SnapshotInfo> snapshotInfos = retrieveSnapshotEntries(snapshotVo.getId(), zoneId);
|
||||
|
||||
boolean result = false;
|
||||
for (var infoEntry : snapshotInfos.entrySet()) {
|
||||
if (BooleanUtils.toBooleanDefaultIfNull(deleteSnapshotInfo(infoEntry.getValue(), infoEntry.getKey(), snapshotVo), false)) {
|
||||
for (var snapshotInfo : snapshotInfos) {
|
||||
if (BooleanUtils.toBooleanDefaultIfNull(deleteSnapshotInfo(snapshotInfo, snapshotVo), false)) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
@ -320,50 +349,53 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
* Destroys the snapshot entry and file.
|
||||
* @return true if destroy successfully, else false.
|
||||
*/
|
||||
protected Boolean deleteSnapshotInfo(SnapshotInfo snapshotInfo, String storage, SnapshotVO snapshotVo) {
|
||||
if (snapshotInfo == null) {
|
||||
s_logger.debug(String.format("Could not find %s entry on %s. Skipping deletion on %s.", snapshotVo, storage, storage));
|
||||
return SECONDARY_STORAGE_SNAPSHOT_ENTRY_IDENTIFIER.equals(storage) ? null : true;
|
||||
}
|
||||
|
||||
protected Boolean deleteSnapshotInfo(SnapshotInfo snapshotInfo, SnapshotVO snapshotVo) {
|
||||
DataStore dataStore = snapshotInfo.getDataStore();
|
||||
String storageToString = String.format("%s {uuid: \"%s\", name: \"%s\"}", storage, dataStore.getUuid(), dataStore.getName());
|
||||
|
||||
String storageToString = String.format("%s {uuid: \"%s\", name: \"%s\"}", dataStore.getRole().name(), dataStore.getUuid(), dataStore.getName());
|
||||
List<SnapshotDataStoreVO> snapshotStoreRefs = snapshotStoreDao.findBySnapshotId(snapshotVo.getId());
|
||||
boolean isLastSnapshotRef = CollectionUtils.isEmpty(snapshotStoreRefs) || snapshotStoreRefs.size() == 1;
|
||||
try {
|
||||
SnapshotObject snapshotObject = castSnapshotInfoToSnapshotObject(snapshotInfo);
|
||||
if (isLastSnapshotRef) {
|
||||
snapshotObject.processEvent(Snapshot.Event.DestroyRequested);
|
||||
|
||||
if (SECONDARY_STORAGE_SNAPSHOT_ENTRY_IDENTIFIER.equals(storage)) {
|
||||
|
||||
}
|
||||
if (!DataStoreRole.Primary.equals(dataStore.getRole())) {
|
||||
verifyIfTheSnapshotIsBeingUsedByAnyVolume(snapshotObject);
|
||||
|
||||
if (deleteSnapshotChain(snapshotInfo, storageToString)) {
|
||||
s_logger.debug(String.format("%s was deleted on %s. We will mark the snapshot as destroyed.", snapshotVo, storageToString));
|
||||
} else {
|
||||
s_logger.debug(String.format("%s was not deleted on %s; however, we will mark the snapshot as destroyed for future garbage collecting.", snapshotVo,
|
||||
storageToString));
|
||||
}
|
||||
|
||||
snapshotStoreDao.updateDisplayForSnapshotStoreRole(snapshotVo.getId(), dataStore.getId(), dataStore.getRole(), false);
|
||||
if (isLastSnapshotRef) {
|
||||
snapshotObject.processEvent(Snapshot.Event.OperationSucceeded);
|
||||
}
|
||||
return true;
|
||||
} else if (deleteSnapshotInPrimaryStorage(snapshotInfo, snapshotVo, storageToString, snapshotObject)) {
|
||||
} else if (deleteSnapshotInPrimaryStorage(snapshotInfo, snapshotVo, storageToString, snapshotObject, isLastSnapshotRef)) {
|
||||
snapshotStoreDao.updateDisplayForSnapshotStoreRole(snapshotVo.getId(), dataStore.getId(), dataStore.getRole(), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
s_logger.debug(String.format("Failed to delete %s on %s.", snapshotVo, storageToString));
|
||||
if (isLastSnapshotRef) {
|
||||
snapshotObject.processEvent(Snapshot.Event.OperationFailed);
|
||||
}
|
||||
} catch (NoTransitionException ex) {
|
||||
s_logger.warn(String.format("Failed to delete %s on %s due to %s.", snapshotVo, storageToString, ex.getMessage()), ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean deleteSnapshotInPrimaryStorage(SnapshotInfo snapshotInfo, SnapshotVO snapshotVo, String storageToString, SnapshotObject snapshotObject) throws NoTransitionException {
|
||||
protected boolean deleteSnapshotInPrimaryStorage(SnapshotInfo snapshotInfo, SnapshotVO snapshotVo,
|
||||
String storageToString, SnapshotObject snapshotObject, boolean isLastSnapshotRef) throws NoTransitionException {
|
||||
try {
|
||||
if (snapshotSvr.deleteSnapshot(snapshotInfo)) {
|
||||
String msg = String.format("%s was deleted on %s.", snapshotVo, storageToString);
|
||||
if (isLastSnapshotRef) {
|
||||
msg = String.format("%s We will mark the snapshot as destroyed.", msg);
|
||||
snapshotObject.processEvent(Snapshot.Event.OperationSucceeded);
|
||||
s_logger.debug(String.format("%s was deleted on %s. We will mark the snapshot as destroyed.", snapshotVo, storageToString));
|
||||
}
|
||||
s_logger.debug(msg);
|
||||
return true;
|
||||
}
|
||||
} catch (CloudRuntimeException ex) {
|
||||
@ -396,18 +428,15 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
/**
|
||||
* Retrieves the snapshot infos on primary and secondary storage.
|
||||
* @param snapshotId The snapshot to retrieve the infos.
|
||||
* @return A map of snapshot infos.
|
||||
* @return A list of snapshot infos.
|
||||
*/
|
||||
protected Map<String, SnapshotInfo> retrieveSnapshotEntries(long snapshotId) {
|
||||
Map<String, SnapshotInfo> snapshotInfos = new LinkedHashMap<>();
|
||||
snapshotInfos.put(SECONDARY_STORAGE_SNAPSHOT_ENTRY_IDENTIFIER, snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Image, false));
|
||||
snapshotInfos.put(PRIMARY_STORAGE_SNAPSHOT_ENTRY_IDENTIFIER, snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary, false));
|
||||
return snapshotInfos;
|
||||
protected List<SnapshotInfo> retrieveSnapshotEntries(long snapshotId, Long zoneId) {
|
||||
return snapshotDataFactory.getSnapshots(snapshotId, zoneId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean revertSnapshot(SnapshotInfo snapshot) {
|
||||
if (canHandle(snapshot, SnapshotOperation.REVERT) == StrategyPriority.CANT_HANDLE) {
|
||||
if (canHandle(snapshot, null, SnapshotOperation.REVERT) == StrategyPriority.CANT_HANDLE) {
|
||||
throw new CloudRuntimeException("Reverting not supported. Create a template or volume based on the snapshot instead.");
|
||||
}
|
||||
|
||||
@ -542,19 +571,31 @@ public class DefaultSnapshotStrategy extends SnapshotStrategyBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) {
|
||||
public StrategyPriority canHandle(Snapshot snapshot, Long zoneId, SnapshotOperation op) {
|
||||
if (SnapshotOperation.REVERT.equals(op)) {
|
||||
long volumeId = snapshot.getVolumeId();
|
||||
VolumeVO volumeVO = volumeDao.findById(volumeId);
|
||||
|
||||
if (volumeVO != null && ImageFormat.QCOW2.equals(volumeVO.getFormat())) {
|
||||
if (isSnapshotStoredOnSameZoneStoreForQCOW2Volume(snapshot, volumeVO)) {
|
||||
return StrategyPriority.DEFAULT;
|
||||
}
|
||||
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
|
||||
if (zoneId != null && SnapshotOperation.DELETE.equals(op)) {
|
||||
s_logger.debug(String.format("canHandle for zone ID: %d, operation: %s - %s", zoneId, op, StrategyPriority.DEFAULT));
|
||||
}
|
||||
return StrategyPriority.DEFAULT;
|
||||
}
|
||||
|
||||
protected boolean isSnapshotStoredOnSameZoneStoreForQCOW2Volume(Snapshot snapshot, VolumeVO volumeVO) {
|
||||
if (volumeVO == null || !ImageFormat.QCOW2.equals(volumeVO.getFormat())) {
|
||||
return false;
|
||||
}
|
||||
List<SnapshotDataStoreVO> snapshotStores = snapshotStoreDao.listBySnapshotIdAndState(snapshot.getId(), State.Ready);
|
||||
return CollectionUtils.isNotEmpty(snapshotStores) &&
|
||||
snapshotStores.stream().anyMatch(s -> Objects.equals(
|
||||
dataStoreMgr.getStoreZoneId(s.getDataStoreId(), s.getRole()), volumeVO.getDataCenterId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ public class ScaleIOSnapshotStrategy extends StorageSystemSnapshotStrategy {
|
||||
private static final Logger LOG = Logger.getLogger(ScaleIOSnapshotStrategy.class);
|
||||
|
||||
@Override
|
||||
public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) {
|
||||
public StrategyPriority canHandle(Snapshot snapshot, Long zoneId, SnapshotOperation op) {
|
||||
long volumeId = snapshot.getVolumeId();
|
||||
VolumeVO volumeVO = volumeDao.findByIdIncludingRemoved(volumeId);
|
||||
boolean baseVolumeExists = volumeVO.getRemoved() == null;
|
||||
@ -53,7 +53,7 @@ public class ScaleIOSnapshotStrategy extends StorageSystemSnapshotStrategy {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
|
||||
if (!isSnapshotStoredOnScaleIOStoragePool(snapshot)) {
|
||||
if (!isSnapshotStoredOnScaleIOStoragePoolAndOperationForSameZone(snapshot, zoneId)) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
|
||||
@ -82,12 +82,18 @@ public class ScaleIOSnapshotStrategy extends StorageSystemSnapshotStrategy {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isSnapshotStoredOnScaleIOStoragePool(Snapshot snapshot) {
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
protected boolean isSnapshotStoredOnScaleIOStoragePoolAndOperationForSameZone(Snapshot snapshot, Long zoneId) {
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshot.getId(), DataStoreRole.Primary);
|
||||
if (snapshotStore == null) {
|
||||
return false;
|
||||
}
|
||||
StoragePoolVO storagePoolVO = primaryDataStoreDao.findById(snapshotStore.getDataStoreId());
|
||||
return storagePoolVO != null && storagePoolVO.getPoolType() == Storage.StoragePoolType.PowerFlex;
|
||||
if (storagePoolVO == null) {
|
||||
return false;
|
||||
}
|
||||
if (zoneId != null && !zoneId.equals(storagePoolVO.getDataCenterId())) {
|
||||
return false;
|
||||
}
|
||||
return storagePoolVO.getPoolType() == Storage.StoragePoolType.PowerFlex;
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotInfo> getSnapshots(long volumeId, DataStoreRole role) {
|
||||
public List<SnapshotInfo> getSnapshotsForVolumeAndStoreRole(long volumeId, DataStoreRole role) {
|
||||
List<SnapshotDataStoreVO> allSnapshotsFromVolumeAndDataStore = snapshotStoreDao.listAllByVolumeAndDataStore(volumeId, role);
|
||||
if (CollectionUtils.isEmpty(allSnapshotsFromVolumeAndDataStore)) {
|
||||
return new ArrayList<>();
|
||||
@ -84,23 +84,90 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotInfo getSnapshot(long snapshotId, DataStoreRole role) {
|
||||
return getSnapshot(snapshotId, role, true);
|
||||
public List<SnapshotInfo> getSnapshots(long snapshotId, Long zoneId) {
|
||||
SnapshotVO snapshot = snapshotDao.findById(snapshotId);
|
||||
if (snapshot == null) { //snapshot may have been removed;
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<SnapshotDataStoreVO> allSnapshotsAndDataStore = snapshotStoreDao.findBySnapshotId(snapshotId);
|
||||
if (CollectionUtils.isEmpty(allSnapshotsAndDataStore)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<SnapshotInfo> infos = new ArrayList<>();
|
||||
for (SnapshotDataStoreVO snapshotDataStoreVO : allSnapshotsAndDataStore) {
|
||||
Long entryZoneId = storeMgr.getStoreZoneId(snapshotDataStoreVO.getDataStoreId(), snapshotDataStoreVO.getRole());
|
||||
if (zoneId != null && !zoneId.equals(entryZoneId)) {
|
||||
continue;
|
||||
}
|
||||
DataStore store = storeMgr.getDataStore(snapshotDataStoreVO.getDataStoreId(), snapshotDataStoreVO.getRole());
|
||||
SnapshotObject info = SnapshotObject.getSnapshotObject(snapshot, store);
|
||||
|
||||
infos.add(info);
|
||||
}
|
||||
return infos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public SnapshotInfo getSnapshot(long snapshotId, DataStoreRole role, boolean retrieveAnySnapshotFromVolume) {
|
||||
public SnapshotInfo getSnapshot(long snapshotId, long storeId, DataStoreRole role) {
|
||||
SnapshotVO snapshot = snapshotDao.findById(snapshotId);
|
||||
if (snapshot == null) {
|
||||
return null;
|
||||
}
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshotId, role);
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findByStoreSnapshot(role, storeId, snapshotId);
|
||||
if (snapshotStore == null) {
|
||||
return null;
|
||||
}
|
||||
DataStore store = storeMgr.getDataStore(snapshotStore.getDataStoreId(), role);
|
||||
return SnapshotObject.getSnapshotObject(snapshot, store);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotInfo getSnapshotWithRoleAndZone(long snapshotId, DataStoreRole role, long zoneId) {
|
||||
return getSnapshot(snapshotId, role, zoneId, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotInfo getSnapshotOnPrimaryStore(long snapshotId) {
|
||||
SnapshotVO snapshot = snapshotDao.findById(snapshotId);
|
||||
if (snapshot == null) {
|
||||
return null;
|
||||
}
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshotId, DataStoreRole.Primary);
|
||||
if (snapshotStore == null) {
|
||||
return null;
|
||||
}
|
||||
DataStore store = storeMgr.getDataStore(snapshotStore.getDataStoreId(), snapshotStore.getRole());
|
||||
SnapshotObject so = SnapshotObject.getSnapshotObject(snapshot, store);
|
||||
return so;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotInfo getSnapshot(long snapshotId, DataStoreRole role, long zoneId, boolean retrieveAnySnapshotFromVolume) {
|
||||
SnapshotVO snapshot = snapshotDao.findById(snapshotId);
|
||||
if (snapshot == null) {
|
||||
return null;
|
||||
}
|
||||
List<SnapshotDataStoreVO> snapshotStores = snapshotStoreDao.listReadyBySnapshot(snapshotId, role);
|
||||
SnapshotDataStoreVO snapshotStore = null;
|
||||
for (SnapshotDataStoreVO ref : snapshotStores) {
|
||||
if (zoneId == storeMgr.getStoreZoneId(ref.getDataStoreId(), ref.getRole())) {
|
||||
snapshotStore = ref;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (snapshotStore == null) {
|
||||
if (!retrieveAnySnapshotFromVolume) {
|
||||
return null;
|
||||
}
|
||||
|
||||
snapshotStore = snapshotStoreDao.findByVolume(snapshotId, snapshot.getVolumeId(), role);
|
||||
snapshotStores = snapshotStoreDao.findByVolume(snapshotId, snapshot.getVolumeId(), role);
|
||||
for (SnapshotDataStoreVO ref : snapshotStores) {
|
||||
if (zoneId == storeMgr.getStoreZoneId(ref.getDataStoreId(), ref.getRole())); {
|
||||
snapshotStore = ref;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (snapshotStore == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
@ -64,6 +65,7 @@ public class SnapshotObject implements SnapshotInfo {
|
||||
private DataStore store;
|
||||
private Object payload;
|
||||
private Boolean fullBackup;
|
||||
private String url;
|
||||
@Inject
|
||||
protected SnapshotDao snapshotDao;
|
||||
@Inject
|
||||
@ -80,8 +82,12 @@ public class SnapshotObject implements SnapshotInfo {
|
||||
SnapshotDataStoreDao snapshotStoreDao;
|
||||
@Inject
|
||||
StorageStrategyFactory storageStrategyFactory;
|
||||
@Inject
|
||||
DataStoreManager dataStoreManager;
|
||||
private String installPath; // temporarily set installPath before passing to resource for entries with empty installPath for object store migration case
|
||||
|
||||
private Long zoneId = null;
|
||||
|
||||
public SnapshotObject() {
|
||||
|
||||
}
|
||||
@ -142,7 +148,7 @@ public class SnapshotObject implements SnapshotInfo {
|
||||
List<SnapshotInfo> children = new ArrayList<>();
|
||||
if (vos != null) {
|
||||
for (SnapshotDataStoreVO vo : vos) {
|
||||
SnapshotInfo info = snapshotFactory.getSnapshot(vo.getSnapshotId(), DataStoreRole.Image);
|
||||
SnapshotInfo info = snapshotFactory.getSnapshot(vo.getSnapshotId(), vo.getDataStoreId(), DataStoreRole.Image);
|
||||
if (info != null) {
|
||||
children.add(info);
|
||||
}
|
||||
@ -164,7 +170,7 @@ public class SnapshotObject implements SnapshotInfo {
|
||||
@Override
|
||||
public long getPhysicalSize() {
|
||||
long physicalSize = 0;
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Image);
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findByStoreSnapshot(DataStoreRole.Image, store.getId(), snapshot.getId());
|
||||
if (snapshotStore != null) {
|
||||
physicalSize = snapshotStore.getPhysicalSize();
|
||||
}
|
||||
@ -194,9 +200,16 @@ public class SnapshotObject implements SnapshotInfo {
|
||||
|
||||
@Override
|
||||
public String getUri() {
|
||||
if (url != null) {
|
||||
return url;
|
||||
}
|
||||
return snapshot.getUuid();
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStore getDataStore() {
|
||||
return store;
|
||||
@ -309,7 +322,10 @@ public class SnapshotObject implements SnapshotInfo {
|
||||
|
||||
@Override
|
||||
public Long getDataCenterId() {
|
||||
return snapshot.getDataCenterId();
|
||||
if (zoneId == null) {
|
||||
zoneId = dataStoreManager.getStoreZoneId(store.getId(), store.getRole());
|
||||
}
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public void processEvent(Snapshot.Event event) throws NoTransitionException {
|
||||
|
||||
@ -25,8 +25,11 @@ import javax.inject.Inject;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
|
||||
@ -42,16 +45,25 @@ import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
||||
import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher;
|
||||
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||
import org.apache.cloudstack.framework.async.AsyncRpcContext;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJob;
|
||||
import org.apache.cloudstack.storage.command.CommandResult;
|
||||
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
|
||||
import org.apache.cloudstack.storage.command.QuerySnapshotZoneCopyAnswer;
|
||||
import org.apache.cloudstack.storage.command.QuerySnapshotZoneCopyCommand;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||
import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
|
||||
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.storage.CreateSnapshotPayload;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.event.UsageEventUtils;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.storage.CreateSnapshotPayload;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
@ -82,6 +94,10 @@ public class SnapshotServiceImpl implements SnapshotService {
|
||||
private SnapshotDetailsDao _snapshotDetailsDao;
|
||||
@Inject
|
||||
VolumeDataFactory volFactory;
|
||||
@Inject
|
||||
EndPointSelector epSelector;
|
||||
@Inject
|
||||
ConfigurationDao _configDao;
|
||||
|
||||
static private class CreateSnapshotContext<T> extends AsyncRpcContext<T> {
|
||||
final SnapshotInfo snapshot;
|
||||
@ -120,6 +136,20 @@ public class SnapshotServiceImpl implements SnapshotService {
|
||||
|
||||
}
|
||||
|
||||
static private class PrepareCopySnapshotContext<T> extends AsyncRpcContext<T> {
|
||||
final SnapshotInfo snapshot;
|
||||
final String copyUrlBase;
|
||||
final AsyncCallFuture<CreateCmdResult> future;
|
||||
|
||||
public PrepareCopySnapshotContext(AsyncCompletionCallback<T> callback, SnapshotInfo snapshot, String copyUrlBase, AsyncCallFuture<CreateCmdResult> future) {
|
||||
super(callback);
|
||||
this.snapshot = snapshot;
|
||||
this.copyUrlBase = copyUrlBase;
|
||||
this.future = future;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static private class RevertSnapshotContext<T> extends AsyncRpcContext<T> {
|
||||
final SnapshotInfo snapshot;
|
||||
final AsyncCallFuture<SnapshotResult> future;
|
||||
@ -132,6 +162,30 @@ public class SnapshotServiceImpl implements SnapshotService {
|
||||
|
||||
}
|
||||
|
||||
private String generateCopyUrlBase(String hostname, String dir) {
|
||||
String scheme = "http";
|
||||
boolean _sslCopy = false;
|
||||
String sslCfg = _configDao.getValue(Config.SecStorageEncryptCopy.toString());
|
||||
String _ssvmUrlDomain = _configDao.getValue("secstorage.ssl.cert.domain");
|
||||
if (sslCfg != null) {
|
||||
_sslCopy = Boolean.parseBoolean(sslCfg);
|
||||
}
|
||||
if(_sslCopy && (_ssvmUrlDomain == null || _ssvmUrlDomain.isEmpty())){
|
||||
s_logger.warn("Empty secondary storage url domain, ignoring SSL");
|
||||
_sslCopy = false;
|
||||
}
|
||||
if (_sslCopy) {
|
||||
if(_ssvmUrlDomain.startsWith("*")) {
|
||||
hostname = hostname.replace(".", "-");
|
||||
hostname = hostname + _ssvmUrlDomain.substring(1);
|
||||
} else {
|
||||
hostname = _ssvmUrlDomain;
|
||||
}
|
||||
scheme = "https";
|
||||
}
|
||||
return scheme + "://" + hostname + "/copy/SecStorage/" + dir;
|
||||
}
|
||||
|
||||
protected Void createSnapshotAsyncCallback(AsyncCallbackDispatcher<SnapshotServiceImpl, CreateCmdResult> callback, CreateSnapshotContext<CreateCmdResult> context) {
|
||||
CreateCmdResult result = callback.getResult();
|
||||
SnapshotObject snapshot = (SnapshotObject)context.snapshot;
|
||||
@ -251,7 +305,13 @@ public class SnapshotServiceImpl implements SnapshotService {
|
||||
// find the image store where the parent snapshot backup is located
|
||||
SnapshotDataStoreVO parentSnapshotOnBackupStore = null;
|
||||
if (parentSnapshot != null) {
|
||||
parentSnapshotOnBackupStore = _snapshotStoreDao.findBySnapshot(parentSnapshot.getId(), DataStoreRole.Image);
|
||||
List<SnapshotDataStoreVO> snaps = _snapshotStoreDao.listReadyBySnapshot(snapshot.getId(), DataStoreRole.Image);
|
||||
for (SnapshotDataStoreVO ref : snaps) {
|
||||
if (snapshot.getDataCenterId() != null && snapshot.getDataCenterId().equals(dataStoreMgr.getStoreZoneId(ref.getDataStoreId(), ref.getRole()))) {
|
||||
parentSnapshotOnBackupStore = ref;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parentSnapshotOnBackupStore == null) {
|
||||
return dataStoreMgr.getImageStoreWithFreeCapacity(snapshot.getDataCenterId());
|
||||
@ -356,6 +416,49 @@ public class SnapshotServiceImpl implements SnapshotService {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Void copySnapshotZoneAsyncCallback(AsyncCallbackDispatcher<SnapshotServiceImpl, CreateCmdResult> callback, CopySnapshotContext<CommandResult> context) {
|
||||
CreateCmdResult result = callback.getResult();
|
||||
SnapshotInfo destSnapshot = context.destSnapshot;
|
||||
AsyncCallFuture<SnapshotResult> future = context.future;
|
||||
SnapshotResult snapResult = new SnapshotResult(destSnapshot, result.getAnswer());
|
||||
if (result.isFailed()) {
|
||||
snapResult.setResult(result.getResult());
|
||||
destSnapshot.processEvent(Event.OperationFailed);
|
||||
future.complete(snapResult);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Answer answer = result.getAnswer();
|
||||
destSnapshot.processEvent(Event.OperationSuccessed);
|
||||
snapResult = new SnapshotResult(_snapshotFactory.getSnapshot(destSnapshot.getId(), destSnapshot.getDataStore()), answer);
|
||||
future.complete(snapResult);
|
||||
} catch (Exception e) {
|
||||
s_logger.debug("Failed to update snapshot state", e);
|
||||
snapResult.setResult(e.toString());
|
||||
future.complete(snapResult);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Void prepareCopySnapshotZoneAsyncCallback(AsyncCallbackDispatcher<SnapshotServiceImpl, QuerySnapshotZoneCopyAnswer> callback, PrepareCopySnapshotContext<CommandResult> context) {
|
||||
QuerySnapshotZoneCopyAnswer answer = callback.getResult();
|
||||
if (answer == null || !answer.getResult()) {
|
||||
CreateCmdResult result = new CreateCmdResult(null, answer);
|
||||
result.setResult(answer != null ? answer.getDetails() : "Unsupported answer");
|
||||
context.future.complete(result);
|
||||
return null;
|
||||
}
|
||||
List<String> files = answer.getFiles();
|
||||
final String copyUrlBase = context.copyUrlBase;
|
||||
StringBuilder url = new StringBuilder();
|
||||
for (String file : files) {
|
||||
url.append(copyUrlBase).append("/").append(file).append("\n");
|
||||
}
|
||||
CreateCmdResult result = new CreateCmdResult(url.toString().trim(), answer);
|
||||
context.future.complete(result);
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Void deleteSnapshotCallback(AsyncCallbackDispatcher<SnapshotServiceImpl, CommandResult> callback, DeleteSnapshotContext<CommandResult> context) {
|
||||
|
||||
CommandResult result = callback.getResult();
|
||||
@ -432,7 +535,7 @@ public class SnapshotServiceImpl implements SnapshotService {
|
||||
@Override
|
||||
public boolean revertSnapshot(SnapshotInfo snapshot) {
|
||||
PrimaryDataStore store = null;
|
||||
SnapshotInfo snapshotOnPrimaryStore = _snapshotFactory.getSnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
SnapshotInfo snapshotOnPrimaryStore = _snapshotFactory.getSnapshotOnPrimaryStore(snapshot.getId());
|
||||
if (snapshotOnPrimaryStore == null) {
|
||||
s_logger.warn("Cannot find an entry for snapshot " + snapshot.getId() + " on primary storage pools, searching with volume's primary storage pool");
|
||||
VolumeInfo volumeInfo = volFactory.getVolume(snapshot.getVolumeId(), DataStoreRole.Primary);
|
||||
@ -608,4 +711,56 @@ public class SnapshotServiceImpl implements SnapshotService {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncCallFuture<SnapshotResult> copySnapshot(SnapshotInfo snapshot, String copyUrl, DataStore store) throws ResourceUnavailableException {
|
||||
SnapshotObject snapshotForCopy = (SnapshotObject)_snapshotFactory.getSnapshot(snapshot, store);
|
||||
snapshotForCopy.setUrl(copyUrl);
|
||||
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Mark snapshot_store_ref entry as Creating");
|
||||
}
|
||||
AsyncCallFuture<SnapshotResult> future = new AsyncCallFuture<SnapshotResult>();
|
||||
DataObject snapshotOnStore = store.create(snapshotForCopy);
|
||||
((SnapshotObject)snapshotOnStore).setUrl(copyUrl);
|
||||
snapshotOnStore.processEvent(Event.CreateOnlyRequested);
|
||||
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Invoke datastore driver createAsync to create snapshot on destination store");
|
||||
}
|
||||
try {
|
||||
CopySnapshotContext<CommandResult> context = new CopySnapshotContext<>(null, (SnapshotObject)snapshotOnStore, snapshotForCopy, future);
|
||||
AsyncCallbackDispatcher<SnapshotServiceImpl, CreateCmdResult> caller = AsyncCallbackDispatcher.create(this);
|
||||
caller.setCallback(caller.getTarget().copySnapshotZoneAsyncCallback(null, null)).setContext(context);
|
||||
store.getDriver().createAsync(store, snapshotOnStore, caller);
|
||||
} catch (CloudRuntimeException ex) {
|
||||
// clean up already persisted snapshot_store_ref entry
|
||||
SnapshotDataStoreVO snapshotStoreVO = _snapshotStoreDao.findByStoreSnapshot(DataStoreRole.Image, store.getId(), snapshot.getId());
|
||||
if (snapshotStoreVO != null) {
|
||||
snapshotForCopy.processEvent(ObjectInDataStoreStateMachine.Event.OperationFailed);
|
||||
}
|
||||
SnapshotResult res = new SnapshotResult((SnapshotObject)snapshotOnStore, null);
|
||||
res.setResult(ex.getMessage());
|
||||
future.complete(res);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncCallFuture<CreateCmdResult> queryCopySnapshot(SnapshotInfo snapshot) throws ResourceUnavailableException {
|
||||
AsyncCallFuture<CreateCmdResult> future = new AsyncCallFuture<>();
|
||||
EndPoint ep = epSelector.select(snapshot);
|
||||
if (ep == null) {
|
||||
s_logger.error(String.format("Failed to find endpoint for generating copy URL for snapshot %d with store %d", snapshot.getId(), snapshot.getDataStore().getId()));
|
||||
throw new ResourceUnavailableException("No secondary VM in running state in source snapshot zone", DataCenter.class, snapshot.getDataCenterId());
|
||||
}
|
||||
DataStore store = snapshot.getDataStore();
|
||||
String copyUrlBase = generateCopyUrlBase(ep.getPublicAddr(), ((ImageStoreEntity)store).getMountPoint());
|
||||
PrepareCopySnapshotContext<CreateCmdResult> context = new PrepareCopySnapshotContext<>(null, snapshot, copyUrlBase, future);
|
||||
AsyncCallbackDispatcher<SnapshotServiceImpl, QuerySnapshotZoneCopyAnswer> caller = AsyncCallbackDispatcher.create(this);
|
||||
caller.setCallback(caller.getTarget().prepareCopySnapshotZoneAsyncCallback(null, null)).setContext(context);
|
||||
caller.setContext(context);
|
||||
QuerySnapshotZoneCopyCommand cmd = new QuerySnapshotZoneCopyCommand((SnapshotObjectTO)(snapshot.getTO()));
|
||||
ep.sendMessageAsync(cmd, caller);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,6 +44,7 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -150,7 +151,7 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSnapshot(Long snapshotId) {
|
||||
public boolean deleteSnapshot(Long snapshotId, Long zoneId) {
|
||||
Preconditions.checkArgument(snapshotId != null, "'snapshotId' cannot be 'null'.");
|
||||
|
||||
SnapshotVO snapshotVO = snapshotDao.findById(snapshotId);
|
||||
@ -181,7 +182,7 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
||||
*/
|
||||
@ActionEvent(eventType = EventTypes.EVENT_SNAPSHOT_OFF_PRIMARY, eventDescription = "deleting snapshot", async = true)
|
||||
private boolean cleanupSnapshotOnPrimaryStore(long snapshotId) {
|
||||
SnapshotObject snapshotObj = (SnapshotObject)snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary);
|
||||
SnapshotObject snapshotObj = (SnapshotObject)snapshotDataFactory.getSnapshotOnPrimaryStore(snapshotId);
|
||||
|
||||
if (snapshotObj == null) {
|
||||
s_logger.debug("Can't find snapshot; deleting it in DB");
|
||||
@ -293,7 +294,7 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
||||
|
||||
verifySnapshotType(snapshotInfo);
|
||||
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshotInfo.getId(), DataStoreRole.Primary);
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshotInfo.getId(), DataStoreRole.Primary);
|
||||
|
||||
if (snapshotStore != null) {
|
||||
long snapshotStoragePoolId = snapshotStore.getDataStoreId();
|
||||
@ -911,7 +912,7 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) {
|
||||
public StrategyPriority canHandle(Snapshot snapshot, Long zoneId, SnapshotOperation op) {
|
||||
Snapshot.LocationType locationType = snapshot.getLocationType();
|
||||
|
||||
// If the snapshot exists on Secondary Storage, we can't delete it.
|
||||
@ -920,20 +921,26 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Image);
|
||||
List<SnapshotDataStoreVO> snapshotOnImageStores = snapshotStoreDao.listReadyBySnapshot(snapshot.getId(), DataStoreRole.Image);
|
||||
|
||||
// If the snapshot exists on Secondary Storage, we can't delete it.
|
||||
if (snapshotStore != null) {
|
||||
if (CollectionUtils.isNotEmpty(snapshotOnImageStores)) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
|
||||
snapshotStore = snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshot.getId(), DataStoreRole.Primary);
|
||||
|
||||
if (snapshotStore == null) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
|
||||
long snapshotStoragePoolId = snapshotStore.getDataStoreId();
|
||||
if (zoneId != null) { // If zoneId is present, then it should be same as the zoneId of primary store
|
||||
StoragePoolVO storagePoolVO = storagePoolDao.findById(snapshotStoragePoolId);
|
||||
if (!zoneId.equals(storagePoolVO.getDataCenterId())) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
boolean storageSystemSupportsCapability = storageSystemSupportsCapability(snapshotStoragePoolId, DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString());
|
||||
|
||||
@ -953,7 +960,7 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
|
||||
boolean acceptableFormat = isAcceptableRevertFormat(volumeVO);
|
||||
|
||||
if (acceptableFormat) {
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshot.getId(), DataStoreRole.Primary);
|
||||
|
||||
boolean usingBackendSnapshot = usingBackendSnapshotFor(snapshot.getId());
|
||||
|
||||
|
||||
@ -55,7 +55,6 @@ import com.cloud.exception.AgentUnavailableException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.storage.CreateSnapshotPayload;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.GuestOSVO;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
@ -390,7 +389,7 @@ public class StorageVMSnapshotStrategy extends DefaultVMSnapshotStrategy {
|
||||
//The snapshot could not be deleted separately, that's why we set snapshot state to BackedUp for operation delete VM snapshots and rollback
|
||||
SnapshotStrategy strategy = storageStrategyFactory.getSnapshotStrategy(snapshot, SnapshotOperation.DELETE);
|
||||
if (strategy != null) {
|
||||
boolean snapshotForDelete = strategy.deleteSnapshot(snapshot.getId());
|
||||
boolean snapshotForDelete = strategy.deleteSnapshot(snapshot.getId(), null);
|
||||
if (!snapshotForDelete) {
|
||||
throw new CloudRuntimeException("Failed to delete snapshot");
|
||||
}
|
||||
@ -415,7 +414,7 @@ public class StorageVMSnapshotStrategy extends DefaultVMSnapshotStrategy {
|
||||
protected void revertDiskSnapshot(VMSnapshot vmSnapshot) {
|
||||
List<VMSnapshotDetailsVO> listSnapshots = vmSnapshotDetailsDao.findDetails(vmSnapshot.getId(), STORAGE_SNAPSHOT);
|
||||
for (VMSnapshotDetailsVO vmSnapshotDetailsVO : listSnapshots) {
|
||||
SnapshotInfo sInfo = snapshotDataFactory.getSnapshot(Long.parseLong(vmSnapshotDetailsVO.getValue()), DataStoreRole.Primary);
|
||||
SnapshotInfo sInfo = snapshotDataFactory.getSnapshotOnPrimaryStore(Long.parseLong(vmSnapshotDetailsVO.getValue()));
|
||||
SnapshotStrategy snapshotStrategy = storageStrategyFactory.getSnapshotStrategy(sInfo, SnapshotOperation.REVERT);
|
||||
if (snapshotStrategy == null) {
|
||||
throw new CloudRuntimeException(String.format("Could not find strategy for snapshot uuid [%s]", sInfo.getId()));
|
||||
|
||||
@ -81,10 +81,10 @@ public class CephSnapshotStrategyTest {
|
||||
VolumeVO volumeVO = Mockito.mock(VolumeVO.class);
|
||||
Mockito.when(volumeVO.getRemoved()).thenReturn(removed);
|
||||
Mockito.when(volumeDao.findByIdIncludingRemoved(Mockito.anyLong())).thenReturn(volumeVO);
|
||||
Mockito.lenient().doReturn(isSnapshotStoredOnRbdStoragePool).when(cephSnapshotStrategy).isSnapshotStoredOnRbdStoragePool(Mockito.any());
|
||||
Mockito.lenient().doReturn(isSnapshotStoredOnRbdStoragePool).when(cephSnapshotStrategy).isSnapshotStoredOnRbdStoragePoolAndOperationForSameZone(Mockito.any(), Mockito.any());
|
||||
|
||||
for (int i = 0; i < snapshotOps.length - 1; i++) {
|
||||
StrategyPriority strategyPriority = cephSnapshotStrategy.canHandle(snapshot, snapshotOps[i]);
|
||||
StrategyPriority strategyPriority = cephSnapshotStrategy.canHandle(snapshot, null, snapshotOps[i]);
|
||||
if (snapshotOps[i] == SnapshotOperation.REVERT && isSnapshotStoredOnRbdStoragePool) {
|
||||
Assert.assertEquals(StrategyPriority.HIGHEST, strategyPriority);
|
||||
} else {
|
||||
|
||||
@ -18,17 +18,16 @@
|
||||
package org.apache.cloudstack.storage.snapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.storage.VolumeDetailVO;
|
||||
import com.cloud.storage.dao.VolumeDetailsDao;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -41,7 +40,13 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.VolumeDetailVO;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.storage.dao.SnapshotZoneDao;
|
||||
import com.cloud.storage.dao.VolumeDetailsDao;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@ -74,27 +79,31 @@ public class DefaultSnapshotStrategyTest {
|
||||
@Mock
|
||||
SnapshotService snapshotServiceMock;
|
||||
|
||||
Map<String, SnapshotInfo> mapStringSnapshotInfoInstance = new LinkedHashMap<>();
|
||||
@Mock
|
||||
SnapshotZoneDao snapshotZoneDaoMock;
|
||||
|
||||
@Mock
|
||||
SnapshotDataStoreDao snapshotDataStoreDao;
|
||||
|
||||
@Mock
|
||||
DataStoreManager dataStoreManager;
|
||||
|
||||
List<SnapshotInfo> mockSnapshotInfos = new ArrayList<>();
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mapStringSnapshotInfoInstance.put("secondary storage", snapshotInfo1Mock);
|
||||
mapStringSnapshotInfoInstance.put("primary storage", snapshotInfo1Mock);
|
||||
mockSnapshotInfos.add(snapshotInfo1Mock);
|
||||
mockSnapshotInfos.add(snapshotInfo2Mock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateRetrieveSnapshotEntries() {
|
||||
Long snapshotId = 1l;
|
||||
Mockito.doReturn(snapshotInfo1Mock, snapshotInfo2Mock).when(snapshotDataFactoryMock).getSnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class), Mockito.anyBoolean());
|
||||
Map<String, SnapshotInfo> result = defaultSnapshotStrategySpy.retrieveSnapshotEntries(snapshotId);
|
||||
Mockito.doReturn(mockSnapshotInfos).when(snapshotDataFactoryMock).getSnapshots(Mockito.anyLong(), Mockito.any());
|
||||
List<SnapshotInfo> result = defaultSnapshotStrategySpy.retrieveSnapshotEntries(snapshotId, null);
|
||||
|
||||
Mockito.verify(snapshotDataFactoryMock).getSnapshot(snapshotId, DataStoreRole.Image, false);
|
||||
Mockito.verify(snapshotDataFactoryMock).getSnapshot(snapshotId, DataStoreRole.Primary, false);
|
||||
|
||||
Assert.assertTrue(result.containsKey("secondary storage"));
|
||||
Assert.assertTrue(result.containsKey("primary storage"));
|
||||
Assert.assertEquals(snapshotInfo1Mock, result.get("secondary storage"));
|
||||
Assert.assertEquals(snapshotInfo2Mock, result.get("primary storage"));
|
||||
Assert.assertTrue(result.contains(snapshotInfo1Mock));
|
||||
Assert.assertTrue(result.contains(snapshotInfo2Mock));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -107,38 +116,29 @@ public class DefaultSnapshotStrategyTest {
|
||||
|
||||
@Test
|
||||
public void validateDestroySnapshotEntriesAndFilesFailToDeleteReturnsFalse() {
|
||||
Mockito.doReturn(false).when(defaultSnapshotStrategySpy).deleteSnapshotInfos(Mockito.any());
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.destroySnapshotEntriesAndFiles(snapshotVoMock));
|
||||
Mockito.doReturn(false).when(defaultSnapshotStrategySpy).deleteSnapshotInfos(Mockito.any(), Mockito.any());
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.destroySnapshotEntriesAndFiles(snapshotVoMock, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateDestroySnapshotEntriesAndFilesDeletesSuccessfullyReturnsTrue() {
|
||||
Mockito.doReturn(true).when(defaultSnapshotStrategySpy).deleteSnapshotInfos(Mockito.any());
|
||||
Assert.assertTrue(defaultSnapshotStrategySpy.destroySnapshotEntriesAndFiles(snapshotVoMock));
|
||||
Mockito.doReturn(true).when(defaultSnapshotStrategySpy).deleteSnapshotInfos(Mockito.any(), Mockito.any());
|
||||
Mockito.doNothing().when(snapshotZoneDaoMock).removeSnapshotFromZones(Mockito.anyLong());
|
||||
Assert.assertTrue(defaultSnapshotStrategySpy.destroySnapshotEntriesAndFiles(snapshotVoMock, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateDeleteSnapshotInfosFailToDeleteReturnsFalse() {
|
||||
Mockito.doReturn(mapStringSnapshotInfoInstance).when(defaultSnapshotStrategySpy).retrieveSnapshotEntries(Mockito.anyLong());
|
||||
Mockito.doReturn(false).when(defaultSnapshotStrategySpy).deleteSnapshotInfo(Mockito.any(), Mockito.anyString(), Mockito.any());
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.deleteSnapshotInfos(snapshotVoMock));
|
||||
Mockito.doReturn(mockSnapshotInfos).when(defaultSnapshotStrategySpy).retrieveSnapshotEntries(Mockito.anyLong(), Mockito.any());
|
||||
Mockito.doReturn(false).when(defaultSnapshotStrategySpy).deleteSnapshotInfo(Mockito.any(), Mockito.any());
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.deleteSnapshotInfos(snapshotVoMock, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateDeleteSnapshotInfosDeletesSuccessfullyReturnsTrue() {
|
||||
Mockito.doReturn(mapStringSnapshotInfoInstance).when(defaultSnapshotStrategySpy).retrieveSnapshotEntries(Mockito.anyLong());
|
||||
Mockito.doReturn(true).when(defaultSnapshotStrategySpy).deleteSnapshotInfo(Mockito.any(), Mockito.anyString(), Mockito.any());
|
||||
Assert.assertTrue(defaultSnapshotStrategySpy.deleteSnapshotInfos(snapshotVoMock));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateDeleteSnapshotInfoSnapshotInfoIsNullOnSecondaryStorageReturnsTrue() {
|
||||
Assert.assertNull(defaultSnapshotStrategySpy.deleteSnapshotInfo(null, "secondary storage", snapshotVoMock));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateDeleteSnapshotInfoSnapshotInfoIsNullOnPrimaryStorageReturnsFalse() {
|
||||
Assert.assertTrue(defaultSnapshotStrategySpy.deleteSnapshotInfo(null, "primary storage", snapshotVoMock));
|
||||
Mockito.doReturn(mockSnapshotInfos).when(defaultSnapshotStrategySpy).retrieveSnapshotEntries(Mockito.anyLong(), Mockito.any());
|
||||
Mockito.doReturn(true).when(defaultSnapshotStrategySpy).deleteSnapshotInfo(Mockito.any(), Mockito.any());
|
||||
Assert.assertTrue(defaultSnapshotStrategySpy.deleteSnapshotInfos(snapshotVoMock, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -147,8 +147,9 @@ public class DefaultSnapshotStrategyTest {
|
||||
Mockito.doReturn(snapshotObjectMock).when(defaultSnapshotStrategySpy).castSnapshotInfoToSnapshotObject(snapshotInfo1Mock);
|
||||
Mockito.doNothing().when(snapshotObjectMock).processEvent(Mockito.any(Snapshot.Event.class));
|
||||
Mockito.doReturn(true).when(snapshotServiceMock).deleteSnapshot(Mockito.any());
|
||||
Mockito.when(dataStoreMock.getRole()).thenReturn(DataStoreRole.Primary);
|
||||
|
||||
boolean result = defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, "primary storage", snapshotVoMock);
|
||||
boolean result = defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, snapshotVoMock);
|
||||
Assert.assertTrue(result);
|
||||
}
|
||||
|
||||
@ -158,8 +159,9 @@ public class DefaultSnapshotStrategyTest {
|
||||
Mockito.doReturn(snapshotObjectMock).when(defaultSnapshotStrategySpy).castSnapshotInfoToSnapshotObject(snapshotInfo1Mock);
|
||||
Mockito.doNothing().when(snapshotObjectMock).processEvent(Mockito.any(Snapshot.Event.class));
|
||||
Mockito.doReturn(false).when(snapshotServiceMock).deleteSnapshot(Mockito.any());
|
||||
Mockito.when(dataStoreMock.getRole()).thenReturn(DataStoreRole.Primary);
|
||||
|
||||
boolean result = defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, "primary storage", snapshotVoMock);
|
||||
boolean result = defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, snapshotVoMock);
|
||||
Assert.assertFalse(result);
|
||||
}
|
||||
|
||||
@ -169,8 +171,9 @@ public class DefaultSnapshotStrategyTest {
|
||||
Mockito.doReturn(snapshotObjectMock).when(defaultSnapshotStrategySpy).castSnapshotInfoToSnapshotObject(snapshotInfo1Mock);
|
||||
Mockito.doNothing().when(snapshotObjectMock).processEvent(Mockito.any(Snapshot.Event.class));
|
||||
Mockito.doThrow(CloudRuntimeException.class).when(snapshotServiceMock).deleteSnapshot(Mockito.any());
|
||||
Mockito.when(dataStoreMock.getRole()).thenReturn(DataStoreRole.Primary);
|
||||
|
||||
boolean result = defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, "primary storage", snapshotVoMock);
|
||||
boolean result = defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, snapshotVoMock);
|
||||
Assert.assertFalse(result);
|
||||
}
|
||||
|
||||
@ -181,8 +184,9 @@ public class DefaultSnapshotStrategyTest {
|
||||
Mockito.doNothing().when(defaultSnapshotStrategySpy).verifyIfTheSnapshotIsBeingUsedByAnyVolume(snapshotObjectMock);
|
||||
Mockito.doNothing().when(snapshotObjectMock).processEvent(Mockito.any(Snapshot.Event.class));
|
||||
Mockito.doReturn(true).when(defaultSnapshotStrategySpy).deleteSnapshotChain(Mockito.any(), Mockito.anyString());
|
||||
Mockito.when(dataStoreMock.getRole()).thenReturn(DataStoreRole.Image);
|
||||
|
||||
boolean result = defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, "secondary storage", snapshotVoMock);
|
||||
boolean result = defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, snapshotVoMock);
|
||||
Assert.assertTrue(result);
|
||||
}
|
||||
|
||||
@ -193,8 +197,9 @@ public class DefaultSnapshotStrategyTest {
|
||||
Mockito.doNothing().when(defaultSnapshotStrategySpy).verifyIfTheSnapshotIsBeingUsedByAnyVolume(snapshotObjectMock);
|
||||
Mockito.doNothing().when(snapshotObjectMock).processEvent(Mockito.any(Snapshot.Event.class));
|
||||
Mockito.doReturn(false).when(defaultSnapshotStrategySpy).deleteSnapshotChain(Mockito.any(), Mockito.anyString());
|
||||
Mockito.when(dataStoreMock.getRole()).thenReturn(DataStoreRole.Image);
|
||||
|
||||
boolean result = defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, "secondary storage", snapshotVoMock);
|
||||
boolean result = defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, snapshotVoMock);
|
||||
Assert.assertTrue(result);
|
||||
}
|
||||
|
||||
@ -203,8 +208,9 @@ public class DefaultSnapshotStrategyTest {
|
||||
Mockito.doReturn(dataStoreMock).when(snapshotInfo1Mock).getDataStore();
|
||||
Mockito.doReturn(snapshotObjectMock).when(defaultSnapshotStrategySpy).castSnapshotInfoToSnapshotObject(snapshotInfo1Mock);
|
||||
Mockito.doThrow(NoTransitionException.class).when(snapshotObjectMock).processEvent(Mockito.any(Snapshot.Event.class));
|
||||
Mockito.when(dataStoreMock.getRole()).thenReturn(DataStoreRole.Image);
|
||||
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, "secondary storage", snapshotVoMock));
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.deleteSnapshotInfo(snapshotInfo1Mock, snapshotVoMock));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -231,18 +237,97 @@ public class DefaultSnapshotStrategyTest {
|
||||
public void deleteSnapshotInPrimaryStorageTestReturnTrueIfDeleteReturnsTrue() throws NoTransitionException {
|
||||
Mockito.doReturn(true).when(snapshotServiceMock).deleteSnapshot(Mockito.any());
|
||||
Mockito.doNothing().when(snapshotObjectMock).processEvent(Mockito.any(Snapshot.Event.class));
|
||||
Assert.assertTrue(defaultSnapshotStrategySpy.deleteSnapshotInPrimaryStorage(null, null, null, snapshotObjectMock));
|
||||
Assert.assertTrue(defaultSnapshotStrategySpy.deleteSnapshotInPrimaryStorage(null, null, null, snapshotObjectMock, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteSnapshotInPrimaryStorageTestReturnTrueIfDeleteNotLastRefReturnsTrue() throws NoTransitionException {
|
||||
Mockito.doReturn(true).when(snapshotServiceMock).deleteSnapshot(Mockito.any());
|
||||
Assert.assertTrue(defaultSnapshotStrategySpy.deleteSnapshotInPrimaryStorage(null, null, null, snapshotObjectMock, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteSnapshotInPrimaryStorageTestReturnFalseIfDeleteReturnsFalse() throws NoTransitionException {
|
||||
Mockito.doReturn(false).when(snapshotServiceMock).deleteSnapshot(Mockito.any());
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.deleteSnapshotInPrimaryStorage(null, null, null, null));
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.deleteSnapshotInPrimaryStorage(null, null, null, null, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteSnapshotInPrimaryStorageTestReturnFalseIfDeleteThrowsException() throws NoTransitionException {
|
||||
Mockito.doThrow(CloudRuntimeException.class).when(snapshotServiceMock).deleteSnapshot(Mockito.any());
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.deleteSnapshotInPrimaryStorage(null, null, null, null));
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.deleteSnapshotInPrimaryStorage(null, null, null, null, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSnapshotImageStoreRefNull() {
|
||||
SnapshotDataStoreVO ref1 = Mockito.mock(SnapshotDataStoreVO.class);
|
||||
Mockito.when(ref1.getDataStoreId()).thenReturn(1L);
|
||||
Mockito.when(ref1.getRole()).thenReturn(DataStoreRole.Image);
|
||||
Mockito.when(snapshotDataStoreDao.listReadyBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class))).thenReturn(List.of(ref1));
|
||||
Mockito.when(dataStoreManager.getStoreZoneId(1L, DataStoreRole.Image)).thenReturn(2L);
|
||||
Assert.assertNull(defaultSnapshotStrategySpy.getSnapshotImageStoreRef(1L, 1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSnapshotImageStoreRefNotNull() {
|
||||
SnapshotDataStoreVO ref1 = Mockito.mock(SnapshotDataStoreVO.class);
|
||||
Mockito.when(ref1.getDataStoreId()).thenReturn(1L);
|
||||
Mockito.when(ref1.getRole()).thenReturn(DataStoreRole.Image);
|
||||
Mockito.when(snapshotDataStoreDao.listReadyBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class))).thenReturn(List.of(ref1));
|
||||
Mockito.when(dataStoreManager.getStoreZoneId(1L, DataStoreRole.Image)).thenReturn(1L);
|
||||
Assert.assertNotNull(defaultSnapshotStrategySpy.getSnapshotImageStoreRef(1L, 1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSnapshotStoredOnSameZoneStoreForQCOW2VolumeNull() {
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.isSnapshotStoredOnSameZoneStoreForQCOW2Volume(Mockito.mock(Snapshot.class), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSnapshotStoredOnSameZoneStoreForQCOW2VolumeVHD() {
|
||||
VolumeVO volumeVO = Mockito.mock((VolumeVO.class));
|
||||
Mockito.when(volumeVO.getFormat()).thenReturn(Storage.ImageFormat.VHD);
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.isSnapshotStoredOnSameZoneStoreForQCOW2Volume(Mockito.mock(Snapshot.class), volumeVO));
|
||||
}
|
||||
|
||||
private void prepareMocksForIsSnapshotStoredOnSameZoneStoreForQCOW2VolumeTest(Long matchingZoneId) {
|
||||
SnapshotDataStoreVO ref1 = Mockito.mock(SnapshotDataStoreVO.class);
|
||||
Mockito.when(ref1.getDataStoreId()).thenReturn(201L);
|
||||
Mockito.when(ref1.getRole()).thenReturn(DataStoreRole.Image);
|
||||
SnapshotDataStoreVO ref2 = Mockito.mock(SnapshotDataStoreVO.class);
|
||||
Mockito.when(ref2.getDataStoreId()).thenReturn(202L);
|
||||
Mockito.when(ref2.getRole()).thenReturn(DataStoreRole.Image);
|
||||
SnapshotDataStoreVO ref3 = Mockito.mock(SnapshotDataStoreVO.class);
|
||||
Mockito.when(ref3.getDataStoreId()).thenReturn(203L);
|
||||
Mockito.when(ref3.getRole()).thenReturn(DataStoreRole.Image);
|
||||
Mockito.when(snapshotDataStoreDao.listBySnapshotIdAndState(1L, ObjectInDataStoreStateMachine.State.Ready)).thenReturn(List.of(ref1, ref2, ref3));
|
||||
Mockito.when(dataStoreManager.getStoreZoneId(201L, DataStoreRole.Image)).thenReturn(111L);
|
||||
Mockito.when(dataStoreManager.getStoreZoneId(202L, DataStoreRole.Image)).thenReturn(matchingZoneId != null ? matchingZoneId : 112L);
|
||||
Mockito.when(dataStoreManager.getStoreZoneId(203L, DataStoreRole.Image)).thenReturn(113L);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSnapshotStoredOnSameZoneStoreForQCOW2VolumeNoRef() {
|
||||
Snapshot snapshot = Mockito.mock((Snapshot.class));
|
||||
Mockito.when(snapshot.getId()).thenReturn(1L);
|
||||
VolumeVO volumeVO = Mockito.mock((VolumeVO.class));
|
||||
Mockito.when(volumeVO.getFormat()).thenReturn(Storage.ImageFormat.QCOW2);
|
||||
Mockito.when(snapshotDataStoreDao.listBySnapshotIdAndState(1L, ObjectInDataStoreStateMachine.State.Ready)).thenReturn(new ArrayList<>());
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.isSnapshotStoredOnSameZoneStoreForQCOW2Volume(snapshot, volumeVO));
|
||||
|
||||
prepareMocksForIsSnapshotStoredOnSameZoneStoreForQCOW2VolumeTest(null);
|
||||
Assert.assertFalse(defaultSnapshotStrategySpy.isSnapshotStoredOnSameZoneStoreForQCOW2Volume(snapshot, volumeVO));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSnapshotStoredOnSameZoneStoreForQCOW2VolumeHasRef() {
|
||||
Snapshot snapshot = Mockito.mock((Snapshot.class));
|
||||
Mockito.when(snapshot.getId()).thenReturn(1L);
|
||||
VolumeVO volumeVO = Mockito.mock((VolumeVO.class));
|
||||
Mockito.when(volumeVO.getFormat()).thenReturn(Storage.ImageFormat.QCOW2);
|
||||
Mockito.when(volumeVO.getDataCenterId()).thenReturn(100L);
|
||||
prepareMocksForIsSnapshotStoredOnSameZoneStoreForQCOW2VolumeTest(100L);
|
||||
Assert.assertTrue(defaultSnapshotStrategySpy.isSnapshotStoredOnSameZoneStoreForQCOW2Volume(snapshot, volumeVO));
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ public class SnapshotDataFactoryImplTest {
|
||||
public void getSnapshotsByVolumeAndDataStoreTestNoSnapshotDataStoreVOFound() {
|
||||
Mockito.doReturn(new ArrayList<>()).when(snapshotStoreDaoMock).listAllByVolumeAndDataStore(volumeMockId, DataStoreRole.Primary);
|
||||
|
||||
List<SnapshotInfo> snapshots = snapshotDataFactoryImpl.getSnapshots(volumeMockId, DataStoreRole.Primary);
|
||||
List<SnapshotInfo> snapshots = snapshotDataFactoryImpl.getSnapshotsForVolumeAndStoreRole(volumeMockId, DataStoreRole.Primary);
|
||||
|
||||
Assert.assertTrue(snapshots.isEmpty());
|
||||
}
|
||||
@ -91,7 +91,7 @@ public class SnapshotDataFactoryImplTest {
|
||||
Mockito.doReturn(dataStoreMock).when(dataStoreManagerMock).getDataStore(dataStoreId, dataStoreRole);
|
||||
Mockito.doReturn(snapshotVoMock).when(snapshotDaoMock).findById(snapshotId);
|
||||
|
||||
List<SnapshotInfo> snapshots = snapshotDataFactoryImpl.getSnapshots(volumeMockId, dataStoreRole);
|
||||
List<SnapshotInfo> snapshots = snapshotDataFactoryImpl.getSnapshotsForVolumeAndStoreRole(volumeMockId, dataStoreRole);
|
||||
|
||||
Assert.assertEquals(1, snapshots.size());
|
||||
|
||||
|
||||
@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.apache.cloudstack.storage.snapshot;
|
||||
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
||||
@ -41,6 +40,8 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
|
||||
public class SnapshotServiceImplTest {
|
||||
@ -65,7 +66,7 @@ public class SnapshotServiceImplTest {
|
||||
|
||||
Mockito.when(snapshot.getId()).thenReturn(1L);
|
||||
Mockito.when(snapshot.getVolumeId()).thenReturn(1L);
|
||||
Mockito.when(_snapshotFactory.getSnapshot(1L, DataStoreRole.Primary)).thenReturn(null);
|
||||
Mockito.when(_snapshotFactory.getSnapshotOnPrimaryStore(1L)).thenReturn(null);
|
||||
Mockito.when(volFactory.getVolume(1L, DataStoreRole.Primary)).thenReturn(volumeInfo);
|
||||
|
||||
PrimaryDataStore store = Mockito.mock(PrimaryDataStore.class);
|
||||
@ -82,5 +83,4 @@ public class SnapshotServiceImplTest {
|
||||
Assert.assertTrue(snapshotService.revertSnapshot(snapshot));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -170,4 +170,16 @@ public class DataStoreManagerImpl implements DataStoreManager {
|
||||
public void setImageDataStoreMgr(ImageStoreProviderManager imageDataStoreMgr) {
|
||||
this.imageDataStoreMgr = imageDataStoreMgr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getStoreZoneId(long storeId, DataStoreRole role) {
|
||||
try {
|
||||
if (role == DataStoreRole.Primary) {
|
||||
return primaryStoreMgr.getPrimaryDataStoreZoneId(storeId);
|
||||
} else {
|
||||
return imageDataStoreMgr.getImageStoreZoneId(storeId);
|
||||
}
|
||||
} catch (CloudRuntimeException ignored) {}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +40,4 @@ public interface ObjectInDataStoreManager {
|
||||
DataObjectInStore findObject(long objId, DataObjectType type, long dataStoreId, DataStoreRole role, String deployAsIsConfiguration);
|
||||
|
||||
DataObjectInStore findObject(DataObject obj, DataStore store);
|
||||
|
||||
DataStore findStore(long objId, DataObjectType type, DataStoreRole role);
|
||||
}
|
||||
|
||||
@ -382,27 +382,4 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStore findStore(long objId, DataObjectType type, DataStoreRole role) {
|
||||
DataStore store = null;
|
||||
if (role == DataStoreRole.Image) {
|
||||
DataObjectInStore vo = null;
|
||||
switch (type) {
|
||||
case TEMPLATE:
|
||||
vo = templateDataStoreDao.findByTemplate(objId, role);
|
||||
break;
|
||||
case SNAPSHOT:
|
||||
vo = snapshotDataStoreDao.findBySnapshot(objId, role);
|
||||
break;
|
||||
case VOLUME:
|
||||
vo = volumeDataStoreDao.findByVolume(objId);
|
||||
break;
|
||||
}
|
||||
if (vo != null) {
|
||||
store = this.storeMgr.getDataStore(vo.getDataStoreId(), role);
|
||||
}
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -30,4 +30,6 @@ public interface PrimaryDataStoreProviderManager {
|
||||
boolean registerDriver(String providerName, PrimaryDataStoreDriver driver);
|
||||
|
||||
boolean registerHostListener(String providerName, HypervisorHostListener listener);
|
||||
|
||||
public long getPrimaryDataStoreZoneId(long dataStoreId);
|
||||
}
|
||||
|
||||
@ -66,10 +66,15 @@ public class StorageStrategyFactoryImpl implements StorageStrategyFactory {
|
||||
|
||||
@Override
|
||||
public SnapshotStrategy getSnapshotStrategy(final Snapshot snapshot, final SnapshotOperation op) {
|
||||
return getSnapshotStrategy(snapshot, null, op);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotStrategy getSnapshotStrategy(Snapshot snapshot, Long zoneId, SnapshotOperation op) {
|
||||
return bestMatch(snapshotStrategies, new CanHandle<SnapshotStrategy>() {
|
||||
@Override
|
||||
public StrategyPriority canHandle(SnapshotStrategy strategy) {
|
||||
return strategy.canHandle(snapshot, op);
|
||||
return strategy.canHandle(snapshot, zoneId, op);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -32,17 +32,11 @@ import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.agent.api.to.NfsTO;
|
||||
import com.cloud.agent.api.to.OVFInformationTO;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Upload;
|
||||
import org.apache.cloudstack.storage.image.deployasis.DeployAsIsHelper;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
|
||||
@ -53,11 +47,15 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.storage.command.CommandResult;
|
||||
import org.apache.cloudstack.storage.command.CopyCommand;
|
||||
import org.apache.cloudstack.storage.command.DeleteCommand;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
|
||||
import org.apache.cloudstack.storage.endpoint.DefaultEndPointSelector;
|
||||
import org.apache.cloudstack.storage.image.deployasis.DeployAsIsHelper;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
@ -68,6 +66,8 @@ import com.cloud.agent.api.storage.GetDatadisksCommand;
|
||||
import com.cloud.agent.api.to.DataObjectType;
|
||||
import com.cloud.agent.api.to.DataTO;
|
||||
import com.cloud.agent.api.to.DatadiskTO;
|
||||
import com.cloud.agent.api.to.NfsTO;
|
||||
import com.cloud.agent.api.to.OVFInformationTO;
|
||||
import com.cloud.alert.AlertManager;
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.exception.AgentUnavailableException;
|
||||
@ -76,7 +76,8 @@ import com.cloud.host.Host;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.secstorage.CommandExecLogDao;
|
||||
import com.cloud.secstorage.CommandExecLogVO;
|
||||
import com.cloud.storage.StorageManager;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Upload;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
@ -84,8 +85,6 @@ import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.storage.dao.VMTemplateZoneDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.storage.download.DownloadMonitor;
|
||||
import com.cloud.user.ResourceLimitService;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.db.TransactionLegacy;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
@ -107,6 +106,8 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
|
||||
@Inject
|
||||
TemplateDataStoreDao _templateStoreDao;
|
||||
@Inject
|
||||
SnapshotDataStoreDao snapshotDataStoreDao;
|
||||
@Inject
|
||||
EndPointSelector _epSelector;
|
||||
@Inject
|
||||
ConfigurationDao configDao;
|
||||
@ -117,21 +118,17 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
|
||||
@Inject
|
||||
DefaultEndPointSelector _defaultEpSelector;
|
||||
@Inject
|
||||
AccountDao _accountDao;
|
||||
@Inject
|
||||
ResourceLimitService _resourceLimitMgr;
|
||||
@Inject
|
||||
DeployAsIsHelper deployAsIsHelper;
|
||||
@Inject
|
||||
HostDao hostDao;
|
||||
@Inject
|
||||
CommandExecLogDao _cmdExecLogDao;
|
||||
@Inject
|
||||
StorageManager storageMgr;
|
||||
@Inject
|
||||
protected SecondaryStorageVmDao _secStorageVmDao;
|
||||
@Inject
|
||||
AgentManager agentMgr;
|
||||
@Inject
|
||||
DataStoreManager dataStoreManager;
|
||||
|
||||
protected String _proxy = null;
|
||||
|
||||
@ -192,6 +189,12 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
|
||||
LOGGER.debug("Downloading volume to data store " + dataStore.getId());
|
||||
}
|
||||
_downloadMonitor.downloadVolumeToStorage(data, caller);
|
||||
} else if (data.getType() == DataObjectType.SNAPSHOT) {
|
||||
caller.setCallback(caller.getTarget().createSnapshotAsyncCallback(null, null));
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Downloading volume to data store " + dataStore.getId());
|
||||
}
|
||||
_downloadMonitor.downloadSnapshotToStorage(data, caller);
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,6 +316,53 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Void createSnapshotAsyncCallback(AsyncCallbackDispatcher<? extends BaseImageStoreDriverImpl, DownloadAnswer> callback, CreateContext<CreateCmdResult> context) {
|
||||
DownloadAnswer answer = callback.getResult();
|
||||
DataObject obj = context.data;
|
||||
DataStore store = obj.getDataStore();
|
||||
|
||||
SnapshotDataStoreVO snapshotStoreVO = snapshotDataStoreDao.findByStoreSnapshot(DataStoreRole.Image, store.getId(), obj.getId());
|
||||
if (snapshotStoreVO != null) {
|
||||
if (VMTemplateStorageResourceAssoc.Status.DOWNLOADED.equals(snapshotStoreVO.getDownloadState())) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Snapshot is already in DOWNLOADED state, ignore further incoming DownloadAnswer");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
SnapshotDataStoreVO updateBuilder = snapshotDataStoreDao.createForUpdate();
|
||||
updateBuilder.setDownloadPercent(answer.getDownloadPct());
|
||||
updateBuilder.setDownloadState(answer.getDownloadStatus());
|
||||
updateBuilder.setLastUpdated(new Date());
|
||||
updateBuilder.setErrorString(answer.getErrorString());
|
||||
updateBuilder.setJobId(answer.getJobId());
|
||||
updateBuilder.setLocalDownloadPath(answer.getDownloadPath());
|
||||
updateBuilder.setInstallPath(answer.getInstallPath());
|
||||
updateBuilder.setSize(answer.getTemplateSize());
|
||||
updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize());
|
||||
snapshotDataStoreDao.update(snapshotStoreVO.getId(), updateBuilder);
|
||||
}
|
||||
|
||||
AsyncCompletionCallback<CreateCmdResult> caller = context.getParentCallback();
|
||||
|
||||
if (List.of(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR,
|
||||
VMTemplateStorageResourceAssoc.Status.ABANDONED,
|
||||
VMTemplateStorageResourceAssoc.Status.UNKNOWN).contains(answer.getDownloadStatus())) {
|
||||
CreateCmdResult result = new CreateCmdResult(null, null);
|
||||
result.setSuccess(false);
|
||||
result.setResult(answer.getErrorString());
|
||||
caller.complete(result);
|
||||
String msg = "Failed to copy snapshot: " + obj.getUuid() + " with error: " + answer.getErrorString();
|
||||
Long zoneId = dataStoreManager.getStoreZoneId(store.getId(), store.getRole());
|
||||
_alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPLOAD_FAILED,
|
||||
zoneId, null, msg, msg);
|
||||
LOGGER.error(msg);
|
||||
} else if (answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED) {
|
||||
CreateCmdResult result = new CreateCmdResult(null, null);
|
||||
caller.complete(result);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CommandResult> callback) {
|
||||
CommandResult result = new CommandResult();
|
||||
@ -331,7 +381,7 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
|
||||
result.setResult(answer.getDetails());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOGGER.debug("Unable to destoy " + data.getType().toString() + ": " + data.getId(), ex);
|
||||
LOGGER.debug("Unable to destroy " + data.getType().toString() + ": " + data.getId(), ex);
|
||||
result.setResult(ex.toString());
|
||||
}
|
||||
callback.complete(result);
|
||||
|
||||
@ -80,4 +80,5 @@ public interface ImageStoreProviderManager {
|
||||
List<DataStore> listImageStoresWithFreeCapacity(List<DataStore> imageStores);
|
||||
|
||||
List<DataStore> orderImageStoresOnFreeCapacity(List<DataStore> imageStores);
|
||||
long getImageStoreZoneId(long dataStoreId);
|
||||
}
|
||||
|
||||
@ -25,14 +25,17 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy.SnapshotOperation;
|
||||
import org.apache.cloudstack.storage.helper.StorageStrategyFactoryImpl;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.storage.Snapshot;
|
||||
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class StrategyPriorityTest {
|
||||
|
||||
@Test
|
||||
@ -42,31 +45,31 @@ public class StrategyPriorityTest {
|
||||
SnapshotStrategy hyperStrategy = mock(SnapshotStrategy.class);
|
||||
SnapshotStrategy highestStrategy = mock(SnapshotStrategy.class);
|
||||
|
||||
doReturn(StrategyPriority.CANT_HANDLE).when(cantHandleStrategy).canHandle(any(Snapshot.class), any(SnapshotOperation.class));
|
||||
doReturn(StrategyPriority.DEFAULT).when(defaultStrategy).canHandle(any(Snapshot.class), any(SnapshotOperation.class));
|
||||
doReturn(StrategyPriority.HYPERVISOR).when(hyperStrategy).canHandle(any(Snapshot.class), any(SnapshotOperation.class));
|
||||
doReturn(StrategyPriority.HIGHEST).when(highestStrategy).canHandle(any(Snapshot.class), any(SnapshotOperation.class));
|
||||
doReturn(StrategyPriority.CANT_HANDLE).when(cantHandleStrategy).canHandle(any(Snapshot.class), Mockito.nullable(Long.class), any(SnapshotStrategy.SnapshotOperation.class));
|
||||
doReturn(StrategyPriority.DEFAULT).when(defaultStrategy).canHandle(any(Snapshot.class), Mockito.nullable(Long.class), any(SnapshotStrategy.SnapshotOperation.class));
|
||||
doReturn(StrategyPriority.HYPERVISOR).when(hyperStrategy).canHandle(any(Snapshot.class), Mockito.nullable(Long.class), any(SnapshotStrategy.SnapshotOperation.class));
|
||||
doReturn(StrategyPriority.HIGHEST).when(highestStrategy).canHandle(any(Snapshot.class), Mockito.nullable(Long.class), any(SnapshotStrategy.SnapshotOperation.class));
|
||||
|
||||
List<SnapshotStrategy> strategies = new ArrayList<SnapshotStrategy>(5);
|
||||
List<SnapshotStrategy> strategies = new ArrayList<>(5);
|
||||
SnapshotStrategy strategy = null;
|
||||
|
||||
StorageStrategyFactoryImpl factory = new StorageStrategyFactoryImpl();
|
||||
factory.setSnapshotStrategies(strategies);
|
||||
|
||||
strategies.add(cantHandleStrategy);
|
||||
strategy = factory.getSnapshotStrategy(mock(Snapshot.class), SnapshotOperation.TAKE);
|
||||
strategy = factory.getSnapshotStrategy(mock(Snapshot.class), SnapshotStrategy.SnapshotOperation.TAKE);
|
||||
assertEquals("A strategy was found when it shouldn't have been.", null, strategy);
|
||||
|
||||
strategies.add(defaultStrategy);
|
||||
strategy = factory.getSnapshotStrategy(mock(Snapshot.class), SnapshotOperation.TAKE);
|
||||
strategy = factory.getSnapshotStrategy(mock(Snapshot.class), SnapshotStrategy.SnapshotOperation.TAKE);
|
||||
assertEquals("Default strategy was not picked.", defaultStrategy, strategy);
|
||||
|
||||
strategies.add(hyperStrategy);
|
||||
strategy = factory.getSnapshotStrategy(mock(Snapshot.class), SnapshotOperation.TAKE);
|
||||
strategy = factory.getSnapshotStrategy(mock(Snapshot.class), SnapshotStrategy.SnapshotOperation.TAKE);
|
||||
assertEquals("Hypervisor strategy was not picked.", hyperStrategy, strategy);
|
||||
|
||||
strategies.add(highestStrategy);
|
||||
strategy = factory.getSnapshotStrategy(mock(Snapshot.class), SnapshotOperation.TAKE);
|
||||
strategy = factory.getSnapshotStrategy(mock(Snapshot.class), SnapshotStrategy.SnapshotOperation.TAKE);
|
||||
assertEquals("Highest strategy was not picked.", highestStrategy, strategy);
|
||||
}
|
||||
|
||||
|
||||
@ -19,12 +19,6 @@
|
||||
|
||||
package org.apache.cloudstack.storage.datastore.db;
|
||||
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -33,6 +27,13 @@ import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SnapshotDataStoreDaoImplTest {
|
||||
|
||||
@ -61,17 +62,15 @@ public class SnapshotDataStoreDaoImplTest {
|
||||
|
||||
@Test
|
||||
public void validateExpungeReferenceBySnapshotIdAndDataStoreRoleNullReference(){
|
||||
Mockito.doReturn(searchCriteriaMock).when(snapshotDataStoreDaoImplSpy).createSearchCriteriaBySnapshotIdAndStoreRole(Mockito.anyLong(), Mockito.any());
|
||||
Mockito.doReturn(null).when(snapshotDataStoreDaoImplSpy).findOneBy(searchCriteriaMock);
|
||||
Assert.assertTrue(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(0, DataStoreRole.Image));
|
||||
Mockito.doReturn(null).when(snapshotDataStoreDaoImplSpy).findByStoreSnapshot(Mockito.any(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Assert.assertTrue(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(0, 1L, DataStoreRole.Image));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateExpungeReferenceBySnapshotIdAndDataStoreRole(){
|
||||
Mockito.doReturn(searchCriteriaMock).when(snapshotDataStoreDaoImplSpy).createSearchCriteriaBySnapshotIdAndStoreRole(Mockito.anyLong(), Mockito.any());
|
||||
Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoImplSpy).findOneBy(searchCriteriaMock);
|
||||
Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoImplSpy).findByStoreSnapshot(Mockito.any(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(true).when(snapshotDataStoreDaoImplSpy).expunge(Mockito.anyLong());
|
||||
Assert.assertTrue(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(0, DataStoreRole.Image));
|
||||
Assert.assertTrue(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(0, 1L, DataStoreRole.Image));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -112,33 +111,30 @@ public class SnapshotDataStoreDaoImplTest {
|
||||
|
||||
@Test
|
||||
public void expungeReferenceBySnapshotIdAndDataStoreRoleTestSnapshotDataStoreIsNullReturnTrue() {
|
||||
Mockito.doReturn(searchCriteriaMock).when(snapshotDataStoreDaoImplSpy).createSearchCriteriaBySnapshotIdAndStoreRole(Mockito.anyLong(), Mockito.any());
|
||||
Mockito.doReturn(null).when(snapshotDataStoreDaoImplSpy).findOneBy(Mockito.any());
|
||||
Mockito.doReturn(null).when(snapshotDataStoreDaoImplSpy).findByStoreSnapshot(Mockito.any(), Mockito.anyLong(), Mockito.anyLong());
|
||||
|
||||
for (DataStoreRole value : DataStoreRole.values()) {
|
||||
Assert.assertTrue(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(1, value));
|
||||
Assert.assertTrue(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(1, 1, value));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expungeReferenceBySnapshotIdAndDataStoreRoleTestSnapshotDataStoreIsNotNullAndExpungeIsTrueReturnTrue() {
|
||||
Mockito.doReturn(searchCriteriaMock).when(snapshotDataStoreDaoImplSpy).createSearchCriteriaBySnapshotIdAndStoreRole(Mockito.anyLong(), Mockito.any());
|
||||
Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoImplSpy).findOneBy(Mockito.any());
|
||||
Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoImplSpy).findByStoreSnapshot(Mockito.any(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(true).when(snapshotDataStoreDaoImplSpy).expunge(Mockito.anyLong());
|
||||
|
||||
for (DataStoreRole value : DataStoreRole.values()) {
|
||||
Assert.assertTrue(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(1, value));
|
||||
Assert.assertTrue(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(1, 1, value));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expungeReferenceBySnapshotIdAndDataStoreRoleTestSnapshotDataStoreIsNotNullAndExpungeIsFalseReturnTrue() {
|
||||
Mockito.doReturn(searchCriteriaMock).when(snapshotDataStoreDaoImplSpy).createSearchCriteriaBySnapshotIdAndStoreRole(Mockito.anyLong(), Mockito.any());
|
||||
Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoImplSpy).findOneBy(Mockito.any());
|
||||
Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoImplSpy).findByStoreSnapshot(Mockito.any(), Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(false).when(snapshotDataStoreDaoImplSpy).expunge(Mockito.anyLong());
|
||||
|
||||
for (DataStoreRole value : DataStoreRole.values()) {
|
||||
Assert.assertFalse(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(1, value));
|
||||
Assert.assertFalse(snapshotDataStoreDaoImplSpy.expungeReferenceBySnapshotIdAndDataStoreRole(1, 1, value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,14 +16,15 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.storage.image.db;
|
||||
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
|
||||
public class TemplateDataStoreDaoImplTest {
|
||||
|
||||
|
||||
@ -85,4 +85,13 @@ public class PrimaryDataStoreProviderManagerImpl implements PrimaryDataStoreProv
|
||||
public boolean registerHostListener(String providerName, HypervisorHostListener listener) {
|
||||
return storageMgr.registerHostListener(providerName, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPrimaryDataStoreZoneId(long dataStoreId) {
|
||||
StoragePoolVO dataStoreVO = dataStoreDao.findByIdIncludingRemoved(dataStoreId);
|
||||
if (dataStoreVO == null) {
|
||||
throw new CloudRuntimeException("Unable to locate datastore with id " + dataStoreId);
|
||||
}
|
||||
return dataStoreVO.getDataCenterId();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
// 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.storage.datastore.manager;
|
||||
|
||||
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PrimaryDataStoreProviderManagerImplTest {
|
||||
|
||||
@Mock
|
||||
PrimaryDataStoreDao primaryDataStoreDao;
|
||||
|
||||
@InjectMocks
|
||||
PrimaryDataStoreProviderManagerImpl primaryDataStoreProviderManager = new PrimaryDataStoreProviderManagerImpl();
|
||||
@Test
|
||||
public void testGetImageStoreZoneId() {
|
||||
final long storeId = 1L;
|
||||
final long zoneId = 1L;
|
||||
StoragePoolVO storagePoolVO = Mockito.mock(StoragePoolVO.class);
|
||||
Mockito.when(storagePoolVO.getDataCenterId()).thenReturn(zoneId);
|
||||
Mockito.when(primaryDataStoreDao.findByIdIncludingRemoved(storeId)).thenReturn(storagePoolVO);
|
||||
long value = primaryDataStoreProviderManager.getPrimaryDataStoreZoneId(storeId);
|
||||
Assert.assertEquals(zoneId, value);
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,8 @@
|
||||
|
||||
package org.apache.cloudstack.framework.jobs.impl;
|
||||
|
||||
import static com.cloud.utils.HumanReadableJson.getHumanReadableBytesJson;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -33,10 +35,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.storage.dao.VolumeDetailsDao;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.NDC;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
||||
@ -49,27 +48,29 @@ import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
||||
import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao;
|
||||
import org.apache.cloudstack.framework.jobs.dao.VmWorkJobDao;
|
||||
import org.apache.cloudstack.framework.jobs.dao.AsyncJobJoinMapDao;
|
||||
import org.apache.cloudstack.framework.jobs.dao.AsyncJobJournalDao;
|
||||
import org.apache.cloudstack.framework.jobs.dao.SyncQueueItemDao;
|
||||
import org.apache.cloudstack.framework.jobs.dao.VmWorkJobDao;
|
||||
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
||||
import org.apache.cloudstack.framework.messagebus.MessageDetector;
|
||||
import org.apache.cloudstack.framework.messagebus.PublishScope;
|
||||
import org.apache.cloudstack.jobs.JobInfo;
|
||||
import org.apache.cloudstack.jobs.JobInfo.Status;
|
||||
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
||||
import org.apache.cloudstack.management.ManagementServerHost;
|
||||
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.MDC;
|
||||
import org.apache.log4j.NDC;
|
||||
|
||||
import com.cloud.cluster.ClusterManagerListener;
|
||||
import org.apache.cloudstack.management.ManagementServerHost;
|
||||
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.storage.dao.SnapshotDetailsDao;
|
||||
import com.cloud.storage.dao.SnapshotDetailsVO;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.storage.dao.VolumeDetailsDao;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.Predicate;
|
||||
@ -94,9 +95,6 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.exception.ExceptionUtil;
|
||||
import com.cloud.utils.mgmt.JmxUtil;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
|
||||
import static com.cloud.utils.HumanReadableJson.getHumanReadableBytesJson;
|
||||
|
||||
public class AsyncJobManagerImpl extends ManagerBase implements AsyncJobManager, ClusterManagerListener, Configurable {
|
||||
// Advanced
|
||||
@ -1115,7 +1113,7 @@ public class AsyncJobManagerImpl extends ManagerBase implements AsyncJobManager,
|
||||
}
|
||||
final List<SnapshotDetailsVO> snapshotList = _snapshotDetailsDao.findDetails(AsyncJob.Constants.MS_ID, Long.toString(msid), false);
|
||||
for (final SnapshotDetailsVO snapshotDetailsVO : snapshotList) {
|
||||
SnapshotInfo snapshot = snapshotFactory.getSnapshot(snapshotDetailsVO.getResourceId(), DataStoreRole.Primary);
|
||||
SnapshotInfo snapshot = snapshotFactory.getSnapshotOnPrimaryStore(snapshotDetailsVO.getResourceId());
|
||||
if (snapshot == null) {
|
||||
_snapshotDetailsDao.remove(snapshotDetailsVO.getId());
|
||||
continue;
|
||||
|
||||
@ -556,22 +556,33 @@ public class PresetVariableHelper {
|
||||
value.setName(snapshotVo.getName());
|
||||
value.setSize(ByteScaleUtils.bytesToMebibytes(snapshotVo.getSize()));
|
||||
value.setSnapshotType(Snapshot.Type.values()[snapshotVo.getSnapshotType()]);
|
||||
value.setStorage(getPresetVariableValueStorage(getSnapshotDataStoreId(snapshotId), usageType));
|
||||
value.setStorage(getPresetVariableValueStorage(getSnapshotDataStoreId(snapshotId, usageRecord.getZoneId()), usageType));
|
||||
value.setTags(getPresetVariableValueResourceTags(snapshotId, ResourceObjectType.Snapshot));
|
||||
}
|
||||
|
||||
protected SnapshotDataStoreVO getSnapshotImageStoreRef(long snapshotId, long zoneId) {
|
||||
List<SnapshotDataStoreVO> snaps = snapshotDataStoreDao.listReadyBySnapshot(snapshotId, DataStoreRole.Image);
|
||||
for (SnapshotDataStoreVO ref : snaps) {
|
||||
ImageStoreVO store = imageStoreDao.findById(ref.getDataStoreId());
|
||||
if (store != null && zoneId == store.getDataCenterId()) {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@link SnapshotInfo#BackupSnapshotAfterTakingSnapshot} is enabled, returns the secondary storage's ID where the snapshot is. Otherwise, returns the primary storage's ID
|
||||
* where the snapshot is.
|
||||
*/
|
||||
protected long getSnapshotDataStoreId(Long snapshotId) {
|
||||
protected long getSnapshotDataStoreId(Long snapshotId, long zoneId) {
|
||||
if (backupSnapshotAfterTakingSnapshot) {
|
||||
SnapshotDataStoreVO snapshotStore = snapshotDataStoreDao.findBySnapshot(snapshotId, DataStoreRole.Image);
|
||||
SnapshotDataStoreVO snapshotStore = getSnapshotImageStoreRef(snapshotId, zoneId);
|
||||
validateIfObjectIsNull(snapshotStore, snapshotId, "data store for snapshot");
|
||||
return snapshotStore.getDataStoreId();
|
||||
}
|
||||
|
||||
SnapshotDataStoreVO snapshotStore = snapshotDataStoreDao.findBySnapshot(snapshotId, DataStoreRole.Primary);
|
||||
SnapshotDataStoreVO snapshotStore = snapshotDataStoreDao.findOneBySnapshotAndDatastoreRole(snapshotId, DataStoreRole.Primary);
|
||||
validateIfObjectIsNull(snapshotStore, snapshotId, "data store for snapshot");
|
||||
return snapshotStore.getDataStoreId();
|
||||
}
|
||||
|
||||
@ -863,7 +863,7 @@ public class PresetVariableHelperTest {
|
||||
Mockito.doReturn(expected.getName()).when(snapshotVoMock).getName();
|
||||
Mockito.doReturn(expected.getSize()).when(snapshotVoMock).getSize();
|
||||
Mockito.doReturn((short) 3).when(snapshotVoMock).getSnapshotType();
|
||||
Mockito.doReturn(1l).when(presetVariableHelperSpy).getSnapshotDataStoreId(Mockito.anyLong());
|
||||
Mockito.doReturn(1l).when(presetVariableHelperSpy).getSnapshotDataStoreId(Mockito.anyLong(), Mockito.anyLong());
|
||||
Mockito.doReturn(expected.getStorage()).when(presetVariableHelperSpy).getPresetVariableValueStorage(Mockito.anyLong(), Mockito.anyInt());
|
||||
Mockito.doReturn(expected.getTags()).when(presetVariableHelperSpy).getPresetVariableValueResourceTags(Mockito.anyLong(), Mockito.any(ResourceObjectType.class));
|
||||
|
||||
@ -891,19 +891,19 @@ public class PresetVariableHelperTest {
|
||||
SnapshotDataStoreVO snapshotDataStoreVoMock = Mockito.mock(SnapshotDataStoreVO.class);
|
||||
|
||||
Long expected = 1l;
|
||||
Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoMock).findBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class));
|
||||
Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoMock).findOneBySnapshotAndDatastoreRole(Mockito.anyLong(), Mockito.any(DataStoreRole.class));
|
||||
Mockito.doReturn(expected).when(snapshotDataStoreVoMock).getDataStoreId();
|
||||
presetVariableHelperSpy.backupSnapshotAfterTakingSnapshot = false;
|
||||
|
||||
Long result = presetVariableHelperSpy.getSnapshotDataStoreId(1l);
|
||||
Long result = presetVariableHelperSpy.getSnapshotDataStoreId(1l, 1l);
|
||||
|
||||
Assert.assertEquals(expected, result);
|
||||
|
||||
Arrays.asList(DataStoreRole.values()).forEach(role -> {
|
||||
if (role == DataStoreRole.Primary) {
|
||||
Mockito.verify(snapshotDataStoreDaoMock).findBySnapshot(Mockito.anyLong(), Mockito.eq(role));
|
||||
Mockito.verify(snapshotDataStoreDaoMock).findOneBySnapshotAndDatastoreRole(Mockito.anyLong(), Mockito.eq(role));
|
||||
} else {
|
||||
Mockito.verify(snapshotDataStoreDaoMock, Mockito.never()).findBySnapshot(Mockito.anyLong(), Mockito.eq(role));
|
||||
Mockito.verify(snapshotDataStoreDaoMock, Mockito.never()).findOneBySnapshotAndDatastoreRole(Mockito.anyLong(), Mockito.eq(role));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -913,19 +913,22 @@ public class PresetVariableHelperTest {
|
||||
SnapshotDataStoreVO snapshotDataStoreVoMock = Mockito.mock(SnapshotDataStoreVO.class);
|
||||
|
||||
Long expected = 2l;
|
||||
Mockito.doReturn(snapshotDataStoreVoMock).when(snapshotDataStoreDaoMock).findBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class));
|
||||
ImageStoreVO imageStore = Mockito.mock(ImageStoreVO.class);
|
||||
Mockito.when(imageStoreDaoMock.findById(Mockito.anyLong())).thenReturn(imageStore);
|
||||
Mockito.when(imageStore.getDataCenterId()).thenReturn(1L);
|
||||
Mockito.when(snapshotDataStoreDaoMock.listReadyBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class))).thenReturn(List.of(snapshotDataStoreVoMock));
|
||||
Mockito.doReturn(expected).when(snapshotDataStoreVoMock).getDataStoreId();
|
||||
presetVariableHelperSpy.backupSnapshotAfterTakingSnapshot = true;
|
||||
|
||||
Long result = presetVariableHelperSpy.getSnapshotDataStoreId(2l);
|
||||
Long result = presetVariableHelperSpy.getSnapshotDataStoreId(2l, 1L);
|
||||
|
||||
Assert.assertEquals(expected, result);
|
||||
|
||||
Arrays.asList(DataStoreRole.values()).forEach(role -> {
|
||||
if (role == DataStoreRole.Image) {
|
||||
Mockito.verify(snapshotDataStoreDaoMock).findBySnapshot(Mockito.anyLong(), Mockito.eq(role));
|
||||
Mockito.verify(snapshotDataStoreDaoMock).listReadyBySnapshot(Mockito.anyLong(), Mockito.eq(role));
|
||||
} else {
|
||||
Mockito.verify(snapshotDataStoreDaoMock, Mockito.never()).findBySnapshot(Mockito.anyLong(), Mockito.eq(role));
|
||||
Mockito.verify(snapshotDataStoreDaoMock, Mockito.never()).listReadyBySnapshot(Mockito.anyLong(), Mockito.eq(role));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1148,4 +1151,26 @@ public class PresetVariableHelperTest {
|
||||
Assert.assertEquals(expected.getExternalId(), result.getExternalId());
|
||||
validateFieldNamesToIncludeInToString(Arrays.asList("id", "name", "externalId"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSnapshotImageStoreRefNull() {
|
||||
SnapshotDataStoreVO ref1 = Mockito.mock(SnapshotDataStoreVO.class);
|
||||
Mockito.when(ref1.getDataStoreId()).thenReturn(1L);
|
||||
Mockito.when(snapshotDataStoreDaoMock.listReadyBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class))).thenReturn(List.of(ref1));
|
||||
ImageStoreVO store = Mockito.mock(ImageStoreVO.class);
|
||||
Mockito.when(store.getDataCenterId()).thenReturn(2L);
|
||||
Mockito.when(imageStoreDaoMock.findById(1L)).thenReturn(store);
|
||||
Assert.assertNull(presetVariableHelperSpy.getSnapshotImageStoreRef(1L, 1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSnapshotImageStoreRefNotNull() {
|
||||
SnapshotDataStoreVO ref1 = Mockito.mock(SnapshotDataStoreVO.class);
|
||||
Mockito.when(ref1.getDataStoreId()).thenReturn(1L);
|
||||
Mockito.when(snapshotDataStoreDaoMock.listReadyBySnapshot(Mockito.anyLong(), Mockito.any(DataStoreRole.class))).thenReturn(List.of(ref1));
|
||||
ImageStoreVO store = Mockito.mock(ImageStoreVO.class);
|
||||
Mockito.when(store.getDataCenterId()).thenReturn(1L);
|
||||
Mockito.when(imageStoreDaoMock.findById(1L)).thenReturn(store);
|
||||
Assert.assertNotNull(presetVariableHelperSpy.getSnapshotImageStoreRef(1L, 1L));
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,17 +19,25 @@
|
||||
package com.cloud.storage.resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.cloud.hypervisor.vmware.manager.VmwareManager;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
|
||||
import org.apache.cloudstack.storage.command.CopyCommand;
|
||||
import org.apache.cloudstack.storage.command.DeleteCommand;
|
||||
import org.apache.cloudstack.storage.command.QuerySnapshotZoneCopyAnswer;
|
||||
import org.apache.cloudstack.storage.command.QuerySnapshotZoneCopyCommand;
|
||||
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||
import org.apache.cloudstack.storage.to.TemplateObjectTO;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.to.DataObjectType;
|
||||
@ -38,9 +46,11 @@ import com.cloud.agent.api.to.DataTO;
|
||||
import com.cloud.agent.api.to.NfsTO;
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.agent.api.to.SwiftTO;
|
||||
import com.cloud.hypervisor.vmware.manager.VmwareManager;
|
||||
import com.cloud.hypervisor.vmware.manager.VmwareStorageManager;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.resource.VmwareStorageProcessor.VmwareStorageProcessorConfigurableFields;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
|
||||
public class VmwareStorageSubsystemCommandHandler extends StorageSubsystemCommandHandlerBase {
|
||||
|
||||
@ -202,4 +212,32 @@ public class VmwareStorageSubsystemCommandHandler extends StorageSubsystemComman
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Answer execute(QuerySnapshotZoneCopyCommand cmd) {
|
||||
SnapshotObjectTO snapshot = cmd.getSnapshot();
|
||||
String parentPath = storageResource.getRootDir(snapshot.getDataStore().getUrl(), _nfsVersion);
|
||||
String path = snapshot.getPath();
|
||||
File snapFile = new File(parentPath + File.separator + path);
|
||||
if (snapFile.exists() && !snapFile.isDirectory()) {
|
||||
return new QuerySnapshotZoneCopyAnswer(cmd, List.of(path));
|
||||
}
|
||||
int index = path.lastIndexOf(File.separator);
|
||||
String snapDir = path.substring(0, index);
|
||||
List<String> files = new ArrayList<>();
|
||||
try (Stream<Path> stream = Files.list(Paths.get(parentPath + File.separator + snapDir))) {
|
||||
List<String> fileNames = stream
|
||||
.filter(file -> !Files.isDirectory(file))
|
||||
.map(Path::getFileName)
|
||||
.map(Path::toString)
|
||||
.collect(Collectors.toList());
|
||||
for (String file : fileNames) {
|
||||
file = snapDir + "/" + file;
|
||||
s_logger.debug(String.format("Found snapshot file %s", file));
|
||||
files.add(file);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
s_logger.error("Error preparing file list for snapshot copy", ioe);
|
||||
}
|
||||
return new QuerySnapshotZoneCopyAnswer(cmd, files);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,13 +22,6 @@ import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.agent.api.storage.MigrateVolumeCommand;
|
||||
import com.cloud.agent.api.storage.ResizeVolumeCommand;
|
||||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
|
||||
@ -69,13 +62,17 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.storage.MigrateVolumeCommand;
|
||||
import com.cloud.agent.api.storage.ResizeVolumeCommand;
|
||||
import com.cloud.agent.api.to.DataObjectType;
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
import com.cloud.agent.api.to.DataTO;
|
||||
import com.cloud.agent.api.to.DiskTO;
|
||||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
import com.cloud.alert.AlertManager;
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.server.ManagementServerImpl;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
@ -97,7 +94,10 @@ import com.cloud.storage.dao.VolumeDetailsDao;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VirtualMachineManager;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||
@ -915,7 +915,7 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||
List<SnapshotVO> snapshots = snapshotDao.listByVolumeId(srcVolumeId);
|
||||
if (CollectionUtils.isNotEmpty(snapshots)) {
|
||||
for (SnapshotVO snapshot : snapshots) {
|
||||
SnapshotDataStoreVO snapshotStore = snapshotDataStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
SnapshotDataStoreVO snapshotStore = snapshotDataStoreDao.findByStoreSnapshot(DataStoreRole.Primary, srcPoolId, snapshot.getId());
|
||||
if (snapshotStore == null) {
|
||||
continue;
|
||||
}
|
||||
@ -1086,7 +1086,7 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||
List<SnapshotVO> snapshots = snapshotDao.listByVolumeId(srcData.getId());
|
||||
if (CollectionUtils.isNotEmpty(snapshots)) {
|
||||
for (SnapshotVO snapshot : snapshots) {
|
||||
SnapshotDataStoreVO snapshotStore = snapshotDataStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
SnapshotDataStoreVO snapshotStore = snapshotDataStoreDao.findByStoreSnapshot(DataStoreRole.Primary, srcPoolId, snapshot.getId());
|
||||
if (snapshotStore == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -18,6 +18,52 @@
|
||||
*/
|
||||
package org.apache.cloudstack.storage.datastore.driver;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.storage.RemoteHostEndPoint;
|
||||
import org.apache.cloudstack.storage.command.CommandResult;
|
||||
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
|
||||
import org.apache.cloudstack.storage.command.CreateObjectAnswer;
|
||||
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.util.StorPoolHelper;
|
||||
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil;
|
||||
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse;
|
||||
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc;
|
||||
import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager;
|
||||
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
|
||||
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||
import org.apache.cloudstack.storage.to.TemplateObjectTO;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.cloudstack.storage.volume.VolumeObject;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.storage.ResizeVolumeAnswer;
|
||||
import com.cloud.agent.api.storage.StorPoolBackupSnapshotCommand;
|
||||
@ -61,50 +107,6 @@ import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.VirtualMachine.State;
|
||||
import com.cloud.vm.VirtualMachineManager;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.storage.RemoteHostEndPoint;
|
||||
import org.apache.cloudstack.storage.command.CommandResult;
|
||||
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
|
||||
import org.apache.cloudstack.storage.command.CreateObjectAnswer;
|
||||
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.util.StorPoolHelper;
|
||||
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil;
|
||||
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse;
|
||||
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc;
|
||||
import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager;
|
||||
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
|
||||
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
|
||||
import org.apache.cloudstack.storage.to.TemplateObjectTO;
|
||||
import org.apache.cloudstack.storage.to.VolumeObjectTO;
|
||||
import org.apache.cloudstack.storage.volume.VolumeObject;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||
|
||||
@ -142,6 +144,18 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||
private StoragePoolDetailsDao storagePoolDetailsDao;
|
||||
@Inject
|
||||
private StoragePoolHostDao storagePoolHostDao;
|
||||
@Inject
|
||||
DataStoreManager dataStoreManager;
|
||||
|
||||
private SnapshotDataStoreVO getSnapshotImageStoreRef(long snapshotId, long zoneId) {
|
||||
List<SnapshotDataStoreVO> snaps = snapshotDataStoreDao.listReadyBySnapshot(snapshotId, DataStoreRole.Image);
|
||||
for (SnapshotDataStoreVO ref : snaps) {
|
||||
if (zoneId == dataStoreManager.getStoreZoneId(ref.getDataStoreId(), ref.getRole())) {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getCapabilities() {
|
||||
@ -468,7 +482,7 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||
} else if (resp.getError().getName().equals("objectDoesNotExist")) {
|
||||
//check if snapshot is on secondary storage
|
||||
StorPoolUtil.spLog("Snapshot %s does not exists on StorPool, will try to create a volume from a snopshot on secondary storage", snapshotName);
|
||||
SnapshotDataStoreVO snap = snapshotDataStoreDao.findBySnapshot(sinfo.getId(), DataStoreRole.Image);
|
||||
SnapshotDataStoreVO snap = getSnapshotImageStoreRef(sinfo.getId(), vinfo.getDataCenterId());
|
||||
if (snap != null && StorPoolStorageAdaptor.getVolumeNameFromPath(snap.getInstallPath(), false) == null) {
|
||||
resp = StorPoolUtil.volumeCreate(srcData.getUuid(), null, size, null, "no", "snapshot", sinfo.getBaseVolume().getMaxIops(), conn);
|
||||
if (resp.getError() == null) {
|
||||
|
||||
@ -16,10 +16,13 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.storage.snapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
||||
@ -36,6 +39,7 @@ import org.apache.cloudstack.storage.datastore.util.StorPoolHelper;
|
||||
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil;
|
||||
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse;
|
||||
import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -48,6 +52,7 @@ import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.SnapshotDao;
|
||||
import com.cloud.storage.dao.SnapshotDetailsDao;
|
||||
import com.cloud.storage.dao.SnapshotDetailsVO;
|
||||
import com.cloud.storage.dao.SnapshotZoneDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
@ -73,6 +78,10 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy {
|
||||
private SnapshotDataFactory snapshotDataFactory;
|
||||
@Inject
|
||||
private StoragePoolDetailsDao storagePoolDetailsDao;
|
||||
@Inject
|
||||
DataStoreManager dataStoreMgr;
|
||||
@Inject
|
||||
SnapshotZoneDao snapshotZoneDao;
|
||||
|
||||
@Override
|
||||
public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) {
|
||||
@ -92,7 +101,7 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSnapshot(Long snapshotId) {
|
||||
public boolean deleteSnapshot(Long snapshotId, Long zoneId) {
|
||||
|
||||
final SnapshotVO snapshotVO = _snapshotDao.findById(snapshotId);
|
||||
VolumeVO volume = _volumeDao.findByIdIncludingRemoved(snapshotVO.getVolumeId());
|
||||
@ -108,11 +117,7 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy {
|
||||
final String err = String.format("Failed to clean-up Storpool snapshot %s. Error: %s", name, resp.getError());
|
||||
StorPoolUtil.spLog(err);
|
||||
} else {
|
||||
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, snapshotVO.getUuid());
|
||||
if (snapshotDetails != null) {
|
||||
_snapshotDetailsDao.removeDetails(snapshotId);
|
||||
}
|
||||
res = deleteSnapshotFromDb(snapshotId);
|
||||
res = deleteSnapshotFromDbIfNeeded(snapshotVO, zoneId);
|
||||
StorPoolUtil.spLog("StorpoolSnapshotStrategy.deleteSnapshot: executed successfully=%s, snapshot uuid=%s, name=%s", res, snapshotVO.getUuid(), name);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -125,13 +130,22 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) {
|
||||
public StrategyPriority canHandle(Snapshot snapshot, Long zoneId, SnapshotOperation op) {
|
||||
log.debug(String.format("StorpoolSnapshotStrategy.canHandle: snapshot=%s, uuid=%s, op=%s", snapshot.getName(), snapshot.getUuid(), op));
|
||||
|
||||
if (op != SnapshotOperation.DELETE) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
|
||||
SnapshotDataStoreVO snapshotOnPrimary = _snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshot.getId(), DataStoreRole.Primary);
|
||||
if (snapshotOnPrimary == null) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
if (zoneId != null) { // If zoneId is present, then it should be same as the zoneId of primary store
|
||||
StoragePoolVO storagePoolVO = _primaryDataStoreDao.findById(snapshotOnPrimary.getDataStoreId());
|
||||
if (!zoneId.equals(storagePoolVO.getDataCenterId())) {
|
||||
return StrategyPriority.CANT_HANDLE;
|
||||
}
|
||||
}
|
||||
String name = StorPoolHelper.getSnapshotName(snapshot.getId(), snapshot.getUuid(), _snapshotStoreDao, _snapshotDetailsDao);
|
||||
if (name != null) {
|
||||
StorPoolUtil.spLog("StorpoolSnapshotStrategy.canHandle: globalId=%s", name);
|
||||
@ -147,6 +161,7 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy {
|
||||
|
||||
private boolean deleteSnapshotChain(SnapshotInfo snapshot) {
|
||||
log.debug("delete snapshot chain for snapshot: " + snapshot.getId());
|
||||
final SnapshotInfo snapOnImage = snapshot;
|
||||
boolean result = false;
|
||||
boolean resultIsSet = false;
|
||||
try {
|
||||
@ -174,8 +189,7 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy {
|
||||
}
|
||||
}
|
||||
if (!deleted) {
|
||||
SnapshotInfo snap = snapshotDataFactory.getSnapshot(snapshot.getId(), DataStoreRole.Image);
|
||||
if (StorPoolStorageAdaptor.getVolumeNameFromPath(snap.getPath(), true) == null) {
|
||||
if (StorPoolStorageAdaptor.getVolumeNameFromPath(snapOnImage.getPath(), true) == null) {
|
||||
try {
|
||||
boolean r = snapshotSvr.deleteSnapshot(snapshot);
|
||||
if (r) {
|
||||
@ -204,8 +218,64 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy {
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean deleteSnapshotFromDb(Long snapshotId) {
|
||||
SnapshotVO snapshotVO = _snapshotDao.findById(snapshotId);
|
||||
protected boolean areLastSnapshotRef(long snapshotId) {
|
||||
List<SnapshotDataStoreVO> snapshotStoreRefs = _snapshotStoreDao.findBySnapshotId(snapshotId);
|
||||
if (CollectionUtils.isEmpty(snapshotStoreRefs) || snapshotStoreRefs.size() == 1) {
|
||||
return true;
|
||||
}
|
||||
return snapshotStoreRefs.size() == 2 && DataStoreRole.Primary.equals(snapshotStoreRefs.get(1).getRole());
|
||||
}
|
||||
|
||||
protected boolean deleteSnapshotOnImageAndPrimary(long snapshotId, DataStore store) {
|
||||
SnapshotInfo snapshotOnImage = snapshotDataFactory.getSnapshot(snapshotId, store);
|
||||
SnapshotObject obj = (SnapshotObject)snapshotOnImage;
|
||||
boolean areLastSnapshotRef = areLastSnapshotRef(snapshotId);
|
||||
try {
|
||||
if (areLastSnapshotRef) {
|
||||
obj.processEvent(Snapshot.Event.DestroyRequested);
|
||||
}
|
||||
} catch (NoTransitionException e) {
|
||||
log.debug("Failed to set the state to destroying: ", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
boolean result = deleteSnapshotChain(snapshotOnImage);
|
||||
_snapshotStoreDao.updateDisplayForSnapshotStoreRole(snapshotId, store.getId(), store.getRole(), false);
|
||||
if (areLastSnapshotRef) {
|
||||
obj.processEvent(Snapshot.Event.OperationSucceeded);
|
||||
}
|
||||
if (result) {
|
||||
SnapshotDataStoreVO snapshotOnPrimary = _snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshotOnImage.getSnapshotId(), DataStoreRole.Primary);
|
||||
if (snapshotOnPrimary != null) {
|
||||
snapshotOnPrimary.setState(State.Destroyed);
|
||||
_snapshotStoreDao.update(snapshotOnPrimary.getId(), snapshotOnPrimary);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to delete snapshot: ", e);
|
||||
try {
|
||||
if (areLastSnapshotRef) {
|
||||
obj.processEvent(Snapshot.Event.OperationFailed);
|
||||
}
|
||||
} catch (NoTransitionException e1) {
|
||||
log.debug("Failed to change snapshot state: " + e.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean deleteSnapshotFromDbIfNeeded(SnapshotVO snapshotVO, Long zoneId) {
|
||||
final long snapshotId = snapshotVO.getId();
|
||||
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, snapshotVO.getUuid());
|
||||
if (snapshotDetails != null) {
|
||||
_snapshotDetailsDao.removeDetails(snapshotId);
|
||||
}
|
||||
|
||||
if (zoneId != null && List.of(Snapshot.State.Allocated, Snapshot.State.CreatedOnPrimary).contains(snapshotVO.getState())) {
|
||||
throw new InvalidParameterValueException(String.format("Snapshot in %s can not be deleted for a zone", snapshotVO.getState()));
|
||||
}
|
||||
|
||||
if (snapshotVO.getState() == Snapshot.State.Allocated) {
|
||||
_snapshotDao.remove(snapshotId);
|
||||
@ -218,10 +288,21 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy {
|
||||
|
||||
if (Snapshot.State.Error.equals(snapshotVO.getState())) {
|
||||
List<SnapshotDataStoreVO> storeRefs = _snapshotStoreDao.findBySnapshotId(snapshotId);
|
||||
List<Long> deletedRefs = new ArrayList<>();
|
||||
for (SnapshotDataStoreVO ref : storeRefs) {
|
||||
_snapshotStoreDao.expunge(ref.getId());
|
||||
boolean refZoneIdMatch = false;
|
||||
if (zoneId != null) {
|
||||
Long refZoneId = dataStoreMgr.getStoreZoneId(ref.getDataStoreId(), ref.getRole());
|
||||
refZoneIdMatch = zoneId.equals(refZoneId);
|
||||
}
|
||||
if (zoneId == null || refZoneIdMatch) {
|
||||
_snapshotStoreDao.expunge(ref.getId());
|
||||
deletedRefs.add(ref.getId());
|
||||
}
|
||||
}
|
||||
if (deletedRefs.size() == storeRefs.size()) {
|
||||
_snapshotDao.remove(snapshotId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -233,45 +314,25 @@ public class StorPoolSnapshotStrategy implements SnapshotStrategy {
|
||||
|
||||
if (!Snapshot.State.BackedUp.equals(snapshotVO.getState()) && !Snapshot.State.Error.equals(snapshotVO.getState()) &&
|
||||
!Snapshot.State.Destroying.equals(snapshotVO.getState())) {
|
||||
throw new InvalidParameterValueException("Can't delete snapshotshot " + snapshotId + " due to it is in " + snapshotVO.getState() + " Status");
|
||||
throw new InvalidParameterValueException("Can't delete snapshot " + snapshotId + " due to it is in " + snapshotVO.getState() + " Status");
|
||||
}
|
||||
List<SnapshotDataStoreVO> storeRefs = _snapshotStoreDao.listReadyBySnapshot(snapshotId, DataStoreRole.Image);
|
||||
if (zoneId != null) {
|
||||
storeRefs.removeIf(ref -> !zoneId.equals(dataStoreMgr.getStoreZoneId(ref.getDataStoreId(), ref.getRole())));
|
||||
}
|
||||
for (SnapshotDataStoreVO ref : storeRefs) {
|
||||
if (!deleteSnapshotOnImageAndPrimary(snapshotId, dataStoreMgr.getDataStore(ref.getDataStoreId(), ref.getRole()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (zoneId != null) {
|
||||
snapshotZoneDao.removeSnapshotFromZone(snapshotVO.getId(), zoneId);
|
||||
} else {
|
||||
snapshotZoneDao.removeSnapshotFromZones(snapshotVO.getId());
|
||||
}
|
||||
|
||||
SnapshotInfo snapshotOnImage = snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Image);
|
||||
if (snapshotOnImage == null) {
|
||||
log.debug("Can't find snapshot on backup storage, delete it in db");
|
||||
_snapshotDao.remove(snapshotId);
|
||||
return true;
|
||||
}
|
||||
|
||||
SnapshotObject obj = (SnapshotObject)snapshotOnImage;
|
||||
try {
|
||||
obj.processEvent(Snapshot.Event.DestroyRequested);
|
||||
} catch (NoTransitionException e) {
|
||||
log.debug("Failed to set the state to destroying: ", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
boolean result = deleteSnapshotChain(snapshotOnImage);
|
||||
obj.processEvent(Snapshot.Event.OperationSucceeded);
|
||||
if (result) {
|
||||
SnapshotDataStoreVO snapshotOnPrimary = _snapshotStoreDao.findBySnapshot(snapshotId, DataStoreRole.Primary);
|
||||
if (snapshotOnPrimary != null) {
|
||||
snapshotOnPrimary.setState(State.Destroyed);
|
||||
_snapshotStoreDao.update(snapshotOnPrimary.getId(), snapshotOnPrimary);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to delete snapshot: ", e);
|
||||
try {
|
||||
obj.processEvent(Snapshot.Event.OperationFailed);
|
||||
} catch (NoTransitionException e1) {
|
||||
log.debug("Failed to change snapshot state: " + e.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) {
|
||||
|
||||
@ -18,8 +18,8 @@
|
||||
|
||||
|
||||
|
||||
# $Id: createtmplt.sh 9132 2010-06-04 20:17:43Z manuel $ $HeadURL: svn://svn.lab.vmops.com/repos/vmdev/java/scripts/storage/secondary/createtmplt.sh $
|
||||
# createtmplt.sh -- install a volume
|
||||
# $Id: createvolume.sh 9132 2010-06-04 20:17:43Z manuel $ $HeadURL: svn://svn.lab.vmops.com/repos/vmdev/java/scripts/storage/secondary/createvolume.sh $
|
||||
# createvolume.sh -- install a volume
|
||||
|
||||
usage() {
|
||||
printf "Usage: %s: -t <volume-fs> -n <volumename> -f <root disk file> -c <md5 cksum> -d <descr> -h [-u] [-v]\n" $(basename $0) >&2
|
||||
|
||||
@ -16,6 +16,78 @@
|
||||
// under the License.
|
||||
package com.cloud.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupResponse;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiConstants.DomainDetails;
|
||||
import org.apache.cloudstack.api.ApiConstants.HostDetails;
|
||||
import org.apache.cloudstack.api.ApiConstants.VMDetails;
|
||||
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||
import org.apache.cloudstack.api.response.AccountResponse;
|
||||
import org.apache.cloudstack.api.response.AsyncJobResponse;
|
||||
import org.apache.cloudstack.api.response.BackupOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.BackupScheduleResponse;
|
||||
import org.apache.cloudstack.api.response.DiskOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.DomainRouterResponse;
|
||||
import org.apache.cloudstack.api.response.EventResponse;
|
||||
import org.apache.cloudstack.api.response.HostForMigrationResponse;
|
||||
import org.apache.cloudstack.api.response.HostResponse;
|
||||
import org.apache.cloudstack.api.response.HostTagResponse;
|
||||
import org.apache.cloudstack.api.response.ImageStoreResponse;
|
||||
import org.apache.cloudstack.api.response.InstanceGroupResponse;
|
||||
import org.apache.cloudstack.api.response.NetworkOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectAccountResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectInvitationResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceIconResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceTagResponse;
|
||||
import org.apache.cloudstack.api.response.SecurityGroupResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||
import org.apache.cloudstack.api.response.StoragePoolResponse;
|
||||
import org.apache.cloudstack.api.response.StorageTagResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
import org.apache.cloudstack.api.response.UserResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||
import org.apache.cloudstack.api.response.VpcOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.backup.Backup;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.backup.BackupSchedule;
|
||||
import org.apache.cloudstack.backup.dao.BackupDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupScheduleDao;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJob;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
||||
import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao;
|
||||
import org.apache.cloudstack.resourcedetail.SnapshotPolicyDetailVO;
|
||||
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
|
||||
import org.apache.cloudstack.resourcedetail.dao.SnapshotPolicyDetailsDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
|
||||
import com.cloud.agent.api.VgpuTypesInfo;
|
||||
import com.cloud.api.query.dao.AccountJoinDao;
|
||||
import com.cloud.api.query.dao.AffinityGroupJoinDao;
|
||||
@ -35,6 +107,7 @@ import com.cloud.api.query.dao.ProjectJoinDao;
|
||||
import com.cloud.api.query.dao.ResourceTagJoinDao;
|
||||
import com.cloud.api.query.dao.SecurityGroupJoinDao;
|
||||
import com.cloud.api.query.dao.ServiceOfferingJoinDao;
|
||||
import com.cloud.api.query.dao.SnapshotJoinDao;
|
||||
import com.cloud.api.query.dao.StoragePoolJoinDao;
|
||||
import com.cloud.api.query.dao.TemplateJoinDao;
|
||||
import com.cloud.api.query.dao.UserAccountJoinDao;
|
||||
@ -60,6 +133,7 @@ import com.cloud.api.query.vo.ProjectJoinVO;
|
||||
import com.cloud.api.query.vo.ResourceTagJoinVO;
|
||||
import com.cloud.api.query.vo.SecurityGroupJoinVO;
|
||||
import com.cloud.api.query.vo.ServiceOfferingJoinVO;
|
||||
import com.cloud.api.query.vo.SnapshotJoinVO;
|
||||
import com.cloud.api.query.vo.StoragePoolJoinVO;
|
||||
import com.cloud.api.query.vo.TemplateJoinVO;
|
||||
import com.cloud.api.query.vo.UserAccountJoinVO;
|
||||
@ -274,72 +348,6 @@ import com.cloud.vm.dao.UserVmDetailsDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.cloud.vm.snapshot.VMSnapshot;
|
||||
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupResponse;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants.DomainDetails;
|
||||
import org.apache.cloudstack.api.ApiConstants.HostDetails;
|
||||
import org.apache.cloudstack.api.ApiConstants.VMDetails;
|
||||
import org.apache.cloudstack.api.ResponseObject.ResponseView;
|
||||
import org.apache.cloudstack.api.response.AccountResponse;
|
||||
import org.apache.cloudstack.api.response.AsyncJobResponse;
|
||||
import org.apache.cloudstack.api.response.BackupOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.BackupResponse;
|
||||
import org.apache.cloudstack.api.response.BackupScheduleResponse;
|
||||
import org.apache.cloudstack.api.response.DiskOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.DomainRouterResponse;
|
||||
import org.apache.cloudstack.api.response.EventResponse;
|
||||
import org.apache.cloudstack.api.response.HostForMigrationResponse;
|
||||
import org.apache.cloudstack.api.response.HostResponse;
|
||||
import org.apache.cloudstack.api.response.HostTagResponse;
|
||||
import org.apache.cloudstack.api.response.ImageStoreResponse;
|
||||
import org.apache.cloudstack.api.response.InstanceGroupResponse;
|
||||
import org.apache.cloudstack.api.response.NetworkOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectAccountResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectInvitationResponse;
|
||||
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceIconResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceTagResponse;
|
||||
import org.apache.cloudstack.api.response.SecurityGroupResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.StoragePoolResponse;
|
||||
import org.apache.cloudstack.api.response.StorageTagResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
import org.apache.cloudstack.api.response.UserResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.api.response.VolumeResponse;
|
||||
import org.apache.cloudstack.api.response.VpcOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.ZoneResponse;
|
||||
import org.apache.cloudstack.backup.Backup;
|
||||
import org.apache.cloudstack.backup.BackupOffering;
|
||||
import org.apache.cloudstack.backup.BackupSchedule;
|
||||
import org.apache.cloudstack.backup.dao.BackupDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupScheduleDao;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJob;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
||||
import org.apache.cloudstack.framework.jobs.dao.AsyncJobDao;
|
||||
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class ApiDBUtils {
|
||||
private static ManagementServer s_ms;
|
||||
@ -441,6 +449,7 @@ public class ApiDBUtils {
|
||||
static AccountJoinDao s_accountJoinDao;
|
||||
static AsyncJobJoinDao s_jobJoinDao;
|
||||
static TemplateJoinDao s_templateJoinDao;
|
||||
static SnapshotJoinDao s_snapshotJoinDao;
|
||||
|
||||
static PhysicalNetworkTrafficTypeDao s_physicalNetworkTrafficTypeDao;
|
||||
static PhysicalNetworkServiceProviderDao s_physicalNetworkServiceProviderDao;
|
||||
@ -471,6 +480,7 @@ public class ApiDBUtils {
|
||||
static BackupOfferingDao s_backupOfferingDao;
|
||||
static NicDao s_nicDao;
|
||||
static ResourceManagerUtil s_resourceManagerUtil;
|
||||
static SnapshotPolicyDetailsDao s_snapshotPolicyDetailsDao;
|
||||
|
||||
@Inject
|
||||
private ManagementServer ms;
|
||||
@ -662,6 +672,8 @@ public class ApiDBUtils {
|
||||
private AsyncJobJoinDao jobJoinDao;
|
||||
@Inject
|
||||
private TemplateJoinDao templateJoinDao;
|
||||
@Inject
|
||||
private SnapshotJoinDao snapshotJoinDao;
|
||||
|
||||
@Inject
|
||||
private PhysicalNetworkTrafficTypeDao physicalNetworkTrafficTypeDao;
|
||||
@ -725,6 +737,8 @@ public class ApiDBUtils {
|
||||
private ResourceIconDao resourceIconDao;
|
||||
@Inject
|
||||
private ResourceManagerUtil resourceManagerUtil;
|
||||
@Inject
|
||||
SnapshotPolicyDetailsDao snapshotPolicyDetailsDao;
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
@ -820,6 +834,7 @@ public class ApiDBUtils {
|
||||
s_accountJoinDao = accountJoinDao;
|
||||
s_jobJoinDao = jobJoinDao;
|
||||
s_templateJoinDao = templateJoinDao;
|
||||
s_snapshotJoinDao = snapshotJoinDao;
|
||||
|
||||
s_physicalNetworkTrafficTypeDao = physicalNetworkTrafficTypeDao;
|
||||
s_physicalNetworkServiceProviderDao = physicalNetworkServiceProviderDao;
|
||||
@ -832,6 +847,7 @@ public class ApiDBUtils {
|
||||
s_vpcOfferingDao = vpcOfferingDao;
|
||||
s_vpcOfferingJoinDao = vpcOfferingJoinDao;
|
||||
s_snapshotPolicyDao = snapshotPolicyDao;
|
||||
s_snapshotPolicyDetailsDao = snapshotPolicyDetailsDao;
|
||||
s_asyncJobDao = asyncJobDao;
|
||||
s_hostDetailsDao = hostDetailsDao;
|
||||
s_clusterDetailsDao = clusterDetailsDao;
|
||||
@ -1649,6 +1665,20 @@ public class ApiDBUtils {
|
||||
return s_snapshotPolicyDao.findById(policyId);
|
||||
}
|
||||
|
||||
public static List<DataCenterVO> findSnapshotPolicyZones(SnapshotPolicy policy, Volume volume) {
|
||||
List<SnapshotPolicyDetailVO> zoneDetails = s_snapshotPolicyDetailsDao.findDetails(policy.getId(), ApiConstants.ZONE_ID);
|
||||
List<Long> zoneIds = new ArrayList<>();
|
||||
for (SnapshotPolicyDetailVO detail : zoneDetails) {
|
||||
try {
|
||||
zoneIds.add(Long.valueOf(detail.getValue()));
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
if (volume != null && !zoneIds.contains(volume.getDataCenterId())) {
|
||||
zoneIds.add(0, volume.getDataCenterId());
|
||||
}
|
||||
return s_zoneDao.listByIds(zoneIds);
|
||||
}
|
||||
|
||||
public static VpcOffering findVpcOfferingById(long offeringId) {
|
||||
return s_vpcOfferingDao.findById(offeringId);
|
||||
}
|
||||
@ -2083,6 +2113,10 @@ public class ApiDBUtils {
|
||||
return s_templateJoinDao.newTemplateResponse(detailsView, view, vr);
|
||||
}
|
||||
|
||||
public static SnapshotResponse newSnapshotResponse(ResponseView view, boolean isShowUnique, SnapshotJoinVO vr) {
|
||||
return s_snapshotJoinDao.newSnapshotResponse(view, isShowUnique, vr);
|
||||
}
|
||||
|
||||
public static TemplateResponse newIsoResponse(TemplateJoinVO vr) {
|
||||
return s_templateJoinDao.newIsoResponse(vr);
|
||||
}
|
||||
@ -2091,6 +2125,10 @@ public class ApiDBUtils {
|
||||
return s_templateJoinDao.setTemplateResponse(detailsView, view, vrData, vr);
|
||||
}
|
||||
|
||||
public static SnapshotResponse fillSnapshotDetails(SnapshotResponse vrData, SnapshotJoinVO vr) {
|
||||
return s_snapshotJoinDao.setSnapshotResponse(vrData, vr);
|
||||
}
|
||||
|
||||
public static List<TemplateJoinVO> newTemplateView(VirtualMachineTemplate vr) {
|
||||
return s_templateJoinDao.newTemplateView(vr);
|
||||
}
|
||||
|
||||
@ -38,7 +38,6 @@ import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
@ -263,6 +262,7 @@ import com.cloud.gpu.GPU;
|
||||
import com.cloud.host.ControlState;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.hypervisor.HypervisorCapabilities;
|
||||
import com.cloud.network.GuestVlan;
|
||||
import com.cloud.network.GuestVlanRange;
|
||||
@ -646,6 +646,7 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
DataCenter zone = ApiDBUtils.findZoneById(volume.getDataCenterId());
|
||||
if (zone != null) {
|
||||
snapshotResponse.setZoneId(zone.getUuid());
|
||||
snapshotResponse.setZoneName(zone.getName());
|
||||
}
|
||||
|
||||
if (volume.getVolumeType() == Volume.Type.ROOT && volume.getInstanceId() != null) {
|
||||
@ -673,7 +674,7 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
} else {
|
||||
DataStoreRole dataStoreRole = getDataStoreRole(snapshot, _snapshotStoreDao, _dataStoreMgr);
|
||||
|
||||
snapshotInfo = snapshotfactory.getSnapshot(snapshot.getId(), dataStoreRole);
|
||||
snapshotInfo = snapshotfactory.getSnapshotWithRoleAndZone(snapshot.getId(), dataStoreRole, volume.getDataCenterId());
|
||||
}
|
||||
|
||||
if (snapshotInfo == null) {
|
||||
@ -681,7 +682,7 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
snapshotResponse.setRevertable(false);
|
||||
} else {
|
||||
snapshotResponse.setRevertable(snapshotInfo.isRevertable());
|
||||
snapshotResponse.setPhysicaSize(snapshotInfo.getPhysicalSize());
|
||||
snapshotResponse.setPhysicalSize(snapshotInfo.getPhysicalSize());
|
||||
}
|
||||
|
||||
// set tag information
|
||||
@ -700,7 +701,7 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
}
|
||||
|
||||
public static DataStoreRole getDataStoreRole(Snapshot snapshot, SnapshotDataStoreDao snapshotStoreDao, DataStoreManager dataStoreMgr) {
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshot.getId(), DataStoreRole.Primary);
|
||||
|
||||
if (snapshotStore == null) {
|
||||
return DataStoreRole.Image;
|
||||
@ -807,6 +808,16 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
CollectionUtils.addIgnoreNull(tagResponses, tagResponse);
|
||||
}
|
||||
policyResponse.setTags(new HashSet<>(tagResponses));
|
||||
List<ZoneResponse> zoneResponses = new ArrayList<>();
|
||||
List<DataCenterVO> zones = ApiDBUtils.findSnapshotPolicyZones(policy, vol);
|
||||
for (DataCenterVO zone : zones) {
|
||||
ZoneResponse zoneResponse = new ZoneResponse();
|
||||
zoneResponse.setId(zone.getUuid());
|
||||
zoneResponse.setName(zone.getName());
|
||||
zoneResponse.setTags(null);
|
||||
zoneResponses.add(zoneResponse);
|
||||
}
|
||||
policyResponse.setZones(new HashSet<>(zoneResponses));
|
||||
|
||||
return policyResponse;
|
||||
}
|
||||
@ -1936,7 +1947,7 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
// it seems that the volume can actually be removed from the DB at some point if it's deleted
|
||||
// if volume comes back null, use another technique to try to discover the zone
|
||||
if (volume == null) {
|
||||
SnapshotDataStoreVO snapshotStore = _snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Primary);
|
||||
SnapshotDataStoreVO snapshotStore = _snapshotStoreDao.findOneBySnapshotAndDatastoreRole(snapshot.getId(), DataStoreRole.Primary);
|
||||
|
||||
if (snapshotStore != null) {
|
||||
long storagePoolId = snapshotStore.getDataStoreId();
|
||||
@ -2837,6 +2848,23 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
response.setDomainName(domain.getName());
|
||||
}
|
||||
|
||||
private void populateOwner(ControlledViewEntityResponse response, ControlledEntity object) {
|
||||
Account account = ApiDBUtils.findAccountById(object.getAccountId());
|
||||
|
||||
if (account.getType() == Account.Type.PROJECT) {
|
||||
// find the project
|
||||
Project project = ApiDBUtils.findProjectByProjectAccountId(account.getId());
|
||||
response.setProjectId(project.getUuid());
|
||||
response.setProjectName(project.getName());
|
||||
} else {
|
||||
response.setAccountName(account.getAccountName());
|
||||
}
|
||||
|
||||
Domain domain = ApiDBUtils.findDomainById(object.getDomainId());
|
||||
response.setDomainId(domain.getUuid());
|
||||
response.setDomainName(domain.getName());
|
||||
}
|
||||
|
||||
public static void populateOwner(ControlledViewEntityResponse response, ControlledViewEntity object) {
|
||||
|
||||
if (object.getAccountType() == Account.Type.PROJECT) {
|
||||
|
||||
@ -79,6 +79,8 @@ import org.apache.cloudstack.api.command.user.project.ListProjectInvitationsCmd;
|
||||
import org.apache.cloudstack.api.command.user.project.ListProjectsCmd;
|
||||
import org.apache.cloudstack.api.command.user.resource.ListDetailOptionsCmd;
|
||||
import org.apache.cloudstack.api.command.user.securitygroup.ListSecurityGroupsCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.CopySnapshotCmd;
|
||||
import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd;
|
||||
import org.apache.cloudstack.api.command.user.tag.ListTagsCmd;
|
||||
import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
|
||||
@ -108,6 +110,7 @@ import org.apache.cloudstack.api.response.ResourceTagResponse;
|
||||
import org.apache.cloudstack.api.response.RouterHealthCheckResultResponse;
|
||||
import org.apache.cloudstack.api.response.SecurityGroupResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||
import org.apache.cloudstack.api.response.StoragePoolResponse;
|
||||
import org.apache.cloudstack.api.response.StorageTagResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
@ -154,6 +157,7 @@ import com.cloud.api.query.dao.ProjectJoinDao;
|
||||
import com.cloud.api.query.dao.ResourceTagJoinDao;
|
||||
import com.cloud.api.query.dao.SecurityGroupJoinDao;
|
||||
import com.cloud.api.query.dao.ServiceOfferingJoinDao;
|
||||
import com.cloud.api.query.dao.SnapshotJoinDao;
|
||||
import com.cloud.api.query.dao.StoragePoolJoinDao;
|
||||
import com.cloud.api.query.dao.TemplateJoinDao;
|
||||
import com.cloud.api.query.dao.UserAccountJoinDao;
|
||||
@ -178,6 +182,7 @@ import com.cloud.api.query.vo.ProjectJoinVO;
|
||||
import com.cloud.api.query.vo.ResourceTagJoinVO;
|
||||
import com.cloud.api.query.vo.SecurityGroupJoinVO;
|
||||
import com.cloud.api.query.vo.ServiceOfferingJoinVO;
|
||||
import com.cloud.api.query.vo.SnapshotJoinVO;
|
||||
import com.cloud.api.query.vo.StoragePoolJoinVO;
|
||||
import com.cloud.api.query.vo.TemplateJoinVO;
|
||||
import com.cloud.api.query.vo.UserAccountJoinVO;
|
||||
@ -228,6 +233,8 @@ import com.cloud.service.dao.ServiceOfferingDetailsDao;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.ScopeType;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.SnapshotVO;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
import com.cloud.storage.Storage.TemplateType;
|
||||
@ -236,6 +243,7 @@ import com.cloud.storage.StoragePoolTagVO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.storage.VolumeApiServiceImpl;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.DiskOfferingDao;
|
||||
import com.cloud.storage.dao.StoragePoolTagsDao;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
@ -461,6 +469,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
@Inject
|
||||
private ManagementServerHostDao msHostDao;
|
||||
|
||||
@Inject
|
||||
private SnapshotJoinDao snapshotJoinDao;
|
||||
|
||||
|
||||
@Inject
|
||||
EntityManager entityManager;
|
||||
@ -3392,6 +3403,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
Account account = CallContext.current().getCallingAccount();
|
||||
Long domainId = cmd.getDomainId();
|
||||
Long id = cmd.getId();
|
||||
List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds());
|
||||
String keyword = cmd.getKeyword();
|
||||
String name = cmd.getName();
|
||||
String networkType = cmd.getNetworkType();
|
||||
@ -3418,6 +3430,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
sc.addAnd("networkType", SearchCriteria.Op.EQ, networkType);
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(ids)) {
|
||||
sc.addAnd("id", SearchCriteria.Op.IN, ids.toArray());
|
||||
}
|
||||
|
||||
if (id != null) {
|
||||
sc.addAnd("id", SearchCriteria.Op.EQ, id);
|
||||
} else if (name != null) {
|
||||
@ -4496,6 +4512,190 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
||||
return responseGenerator.createHealthCheckResponse(_routerDao.findById(routerId), result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListResponse<SnapshotResponse> listSnapshots(ListSnapshotsCmd cmd) {
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
Pair<List<SnapshotJoinVO>, Integer> result = searchForSnapshotsWithParams(cmd.getId(), cmd.getIds(),
|
||||
cmd.getVolumeId(), cmd.getSnapshotName(), cmd.getKeyword(), cmd.getTags(),
|
||||
cmd.getSnapshotType(), cmd.getIntervalType(), cmd.getZoneId(), cmd.getLocationType(),
|
||||
cmd.isShowUnique(), cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId(),
|
||||
cmd.getStartIndex(), cmd.getPageSizeVal(), cmd.listAll(), cmd.isRecursive(), caller);
|
||||
ListResponse<SnapshotResponse> response = new ListResponse<>();
|
||||
ResponseView respView = ResponseView.Restricted;
|
||||
if (CallContext.current().getCallingAccount().getType() == Account.Type.ADMIN) {
|
||||
respView = ResponseView.Full;
|
||||
}
|
||||
List<SnapshotResponse> templateResponses = ViewResponseHelper.createSnapshotResponse(respView, cmd.isShowUnique(), result.first().toArray(new SnapshotJoinVO[result.first().size()]));
|
||||
response.setResponses(templateResponses, result.second());
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotResponse listSnapshot(CopySnapshotCmd cmd) {
|
||||
Account caller = CallContext.current().getCallingAccount();
|
||||
List<Long> zoneIds = cmd.getDestinationZoneIds();
|
||||
Pair<List<SnapshotJoinVO>, Integer> result = searchForSnapshotsWithParams(cmd.getId(), null,
|
||||
null, null, null, null,
|
||||
null, null, zoneIds.get(0), Snapshot.LocationType.SECONDARY.name(),
|
||||
false, null, null, null,
|
||||
null, null, true, false, caller);
|
||||
ResponseView respView = ResponseView.Restricted;
|
||||
if (CallContext.current().getCallingAccount().getType() == Account.Type.ADMIN) {
|
||||
respView = ResponseView.Full;
|
||||
}
|
||||
List<SnapshotResponse> templateResponses = ViewResponseHelper.createSnapshotResponse(respView, false, result.first().get(0));
|
||||
return templateResponses.get(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Pair<List<SnapshotJoinVO>, Integer> searchForSnapshotsWithParams(final Long id, List<Long> ids,
|
||||
final Long volumeId, final String name, final String keyword, final Map<String, String> tags,
|
||||
final String snapshotTypeStr, final String intervalTypeStr, final Long zoneId, final String locationTypeStr,
|
||||
final boolean isShowUnique, final String accountName, Long domainId, final Long projectId,
|
||||
final Long startIndex, final Long pageSize,final boolean listAll, boolean isRecursive, final Account caller) {
|
||||
ids = getIdsListFromCmd(id, ids);
|
||||
Snapshot.LocationType locationType = null;
|
||||
if (locationTypeStr != null) {
|
||||
try {
|
||||
locationType = Snapshot.LocationType.valueOf(locationTypeStr.trim().toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid %s specified, %s", ApiConstants.LOCATION_TYPE, locationTypeStr));
|
||||
}
|
||||
}
|
||||
|
||||
Filter searchFilter = new Filter(SnapshotJoinVO.class, "snapshotStorePair", SortKeyAscending.value(), startIndex, pageSize);
|
||||
|
||||
List<Long> permittedAccountIds = new ArrayList<>();
|
||||
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(domainId, isRecursive, null);
|
||||
_accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccountIds, domainIdRecursiveListProject, listAll, false);
|
||||
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
|
||||
domainId = domainIdRecursiveListProject.first();
|
||||
isRecursive = domainIdRecursiveListProject.second();
|
||||
// Verify parameters
|
||||
if (volumeId != null) {
|
||||
VolumeVO volume = volumeDao.findById(volumeId);
|
||||
if (volume != null) {
|
||||
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, volume);
|
||||
}
|
||||
}
|
||||
|
||||
SearchBuilder<SnapshotJoinVO> sb = snapshotJoinDao.createSearchBuilder();
|
||||
if (isShowUnique) {
|
||||
sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct snapshotId
|
||||
} else {
|
||||
sb.select(null, Func.DISTINCT, sb.entity().getSnapshotStorePair()); // select distinct (snapshotId, store_role, store_id) key
|
||||
}
|
||||
_accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccountIds, listProjectResourcesCriteria);
|
||||
sb.and("statusNEQ", sb.entity().getStatus(), SearchCriteria.Op.NEQ); //exclude those Destroyed snapshot, not showing on UI
|
||||
sb.and("volumeId", sb.entity().getVolumeId(), SearchCriteria.Op.EQ);
|
||||
sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
|
||||
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
|
||||
sb.and("idIN", sb.entity().getId(), SearchCriteria.Op.IN);
|
||||
sb.and("snapshotTypeEQ", sb.entity().getSnapshotType(), SearchCriteria.Op.IN);
|
||||
sb.and("snapshotTypeNEQ", sb.entity().getSnapshotType(), SearchCriteria.Op.NIN);
|
||||
sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ);
|
||||
sb.and("locationType", sb.entity().getStoreRole(), SearchCriteria.Op.EQ);
|
||||
|
||||
if (tags != null && !tags.isEmpty()) {
|
||||
SearchBuilder<ResourceTagVO> tagSearch = _resourceTagDao.createSearchBuilder();
|
||||
for (int count = 0; count < tags.size(); count++) {
|
||||
tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ);
|
||||
tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ);
|
||||
tagSearch.cp();
|
||||
}
|
||||
tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
||||
sb.groupBy(sb.entity().getId());
|
||||
sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER);
|
||||
}
|
||||
|
||||
SearchCriteria<SnapshotJoinVO> sc = sb.create();
|
||||
_accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccountIds, listProjectResourcesCriteria);
|
||||
|
||||
sc.setParameters("statusNEQ", Snapshot.State.Destroyed);
|
||||
|
||||
if (volumeId != null) {
|
||||
sc.setParameters("volumeId", volumeId);
|
||||
}
|
||||
|
||||
if (tags != null && !tags.isEmpty()) {
|
||||
int count = 0;
|
||||
sc.setJoinParameters("tagSearch", "resourceType", ResourceObjectType.Snapshot.toString());
|
||||
for (String key : tags.keySet()) {
|
||||
sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key);
|
||||
sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (zoneId != null) {
|
||||
sc.setParameters("dataCenterId", zoneId);
|
||||
}
|
||||
|
||||
setIdsListToSearchCriteria(sc, ids);
|
||||
|
||||
if (name != null) {
|
||||
sc.setParameters("name", name);
|
||||
}
|
||||
|
||||
if (id != null) {
|
||||
sc.setParameters("id", id);
|
||||
}
|
||||
|
||||
if (locationType != null) {
|
||||
sc.setParameters("locationType", Snapshot.LocationType.PRIMARY.equals(locationType) ? locationType.name() : DataStoreRole.Image.name());
|
||||
}
|
||||
|
||||
if (keyword != null) {
|
||||
SearchCriteria<SnapshotJoinVO> ssc = snapshotJoinDao.createSearchCriteria();
|
||||
ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
||||
sc.addAnd("name", SearchCriteria.Op.SC, ssc);
|
||||
}
|
||||
|
||||
if (snapshotTypeStr != null) {
|
||||
Snapshot.Type snapshotType = SnapshotVO.getSnapshotType(snapshotTypeStr);
|
||||
if (snapshotType == null) {
|
||||
throw new InvalidParameterValueException("Unsupported snapshot type " + snapshotTypeStr);
|
||||
}
|
||||
if (snapshotType == Snapshot.Type.RECURRING) {
|
||||
sc.setParameters("snapshotTypeEQ", Snapshot.Type.HOURLY.ordinal(), Snapshot.Type.DAILY.ordinal(), Snapshot.Type.WEEKLY.ordinal(), Snapshot.Type.MONTHLY.ordinal());
|
||||
} else {
|
||||
sc.setParameters("snapshotTypeEQ", snapshotType.ordinal());
|
||||
}
|
||||
} else if (intervalTypeStr != null && volumeId != null) {
|
||||
Snapshot.Type type = SnapshotVO.getSnapshotType(intervalTypeStr);
|
||||
if (type == null) {
|
||||
throw new InvalidParameterValueException("Unsupported snapshot interval type " + intervalTypeStr);
|
||||
}
|
||||
sc.setParameters("snapshotTypeEQ", type.ordinal());
|
||||
} else {
|
||||
// Show only MANUAL and RECURRING snapshot types
|
||||
sc.setParameters("snapshotTypeNEQ", Snapshot.Type.TEMPLATE.ordinal(), Snapshot.Type.GROUP.ordinal());
|
||||
}
|
||||
|
||||
Pair<List<SnapshotJoinVO>, Integer> snapshotDataPair;
|
||||
if (isShowUnique) {
|
||||
snapshotDataPair = snapshotJoinDao.searchAndDistinctCount(sc, searchFilter, new String[]{"snapshot_view.id"});
|
||||
} else {
|
||||
snapshotDataPair = snapshotJoinDao.searchAndDistinctCount(sc, searchFilter, new String[]{"snapshot_view.snapshot_store_pair"});
|
||||
}
|
||||
|
||||
Integer count = snapshotDataPair.second();
|
||||
if (count == 0) {
|
||||
// empty result
|
||||
return snapshotDataPair;
|
||||
}
|
||||
List<SnapshotJoinVO> snapshotData = snapshotDataPair.first();
|
||||
List<SnapshotJoinVO> snapshots;
|
||||
if (isShowUnique) {
|
||||
snapshots = snapshotJoinDao.findByDistinctIds(zoneId, snapshotData.stream().map(SnapshotJoinVO::getId).toArray(Long[]::new));
|
||||
} else {
|
||||
snapshots = snapshotJoinDao.searchBySnapshotStorePair(snapshotData.stream().map(SnapshotJoinVO::getSnapshotStorePair).toArray(String[]::new));
|
||||
}
|
||||
|
||||
return new Pair<>(snapshots, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigComponentName() {
|
||||
return QueryService.class.getSimpleName();
|
||||
|
||||
@ -49,6 +49,7 @@ import org.apache.cloudstack.api.response.ProjectResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceTagResponse;
|
||||
import org.apache.cloudstack.api.response.SecurityGroupResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||
import org.apache.cloudstack.api.response.StoragePoolResponse;
|
||||
import org.apache.cloudstack.api.response.StorageTagResponse;
|
||||
import org.apache.cloudstack.api.response.TemplateResponse;
|
||||
@ -78,6 +79,7 @@ import com.cloud.api.query.vo.ProjectJoinVO;
|
||||
import com.cloud.api.query.vo.ResourceTagJoinVO;
|
||||
import com.cloud.api.query.vo.SecurityGroupJoinVO;
|
||||
import com.cloud.api.query.vo.ServiceOfferingJoinVO;
|
||||
import com.cloud.api.query.vo.SnapshotJoinVO;
|
||||
import com.cloud.api.query.vo.StoragePoolJoinVO;
|
||||
import com.cloud.api.query.vo.TemplateJoinVO;
|
||||
import com.cloud.api.query.vo.UserAccountJoinVO;
|
||||
@ -592,6 +594,23 @@ public class ViewResponseHelper {
|
||||
return new ArrayList<TemplateResponse>(vrDataList.values());
|
||||
}
|
||||
|
||||
public static List<SnapshotResponse> createSnapshotResponse(ResponseView view, boolean isShowUnique, SnapshotJoinVO... snapshots) {
|
||||
LinkedHashMap<String, SnapshotResponse> vrDataList = new LinkedHashMap<>();
|
||||
for (SnapshotJoinVO vr : snapshots) {
|
||||
SnapshotResponse vrData = vrDataList.get(vr.getSnapshotStorePair());
|
||||
if (vrData == null) {
|
||||
// first time encountering this snapshot
|
||||
vrData = ApiDBUtils.newSnapshotResponse(view, isShowUnique, vr);
|
||||
}
|
||||
else{
|
||||
// update tags
|
||||
vrData = ApiDBUtils.fillSnapshotDetails(vrData, vr);
|
||||
}
|
||||
vrDataList.put(vr.getSnapshotStorePair(), vrData);
|
||||
}
|
||||
return new ArrayList<SnapshotResponse>(vrDataList.values());
|
||||
}
|
||||
|
||||
public static List<TemplateResponse> createTemplateUpdateResponse(ResponseView view, TemplateJoinVO... templates) {
|
||||
LinkedHashMap<Long, TemplateResponse> vrDataList = new LinkedHashMap<>();
|
||||
for (TemplateJoinVO vr : templates) {
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
// 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 com.cloud.api.query.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.api.ResponseObject;
|
||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||
|
||||
import com.cloud.api.query.vo.SnapshotJoinVO;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
public interface SnapshotJoinDao extends GenericDao<SnapshotJoinVO, Long> {
|
||||
|
||||
SnapshotResponse newSnapshotResponse(ResponseObject.ResponseView view, boolean isShowUnique, SnapshotJoinVO snapshotJoinVO);
|
||||
|
||||
SnapshotResponse setSnapshotResponse(SnapshotResponse snapshotResponse, SnapshotJoinVO snapshot);
|
||||
|
||||
Pair<List<SnapshotJoinVO>, Integer> searchIncludingRemovedAndCount(final SearchCriteria<SnapshotJoinVO> sc, final Filter filter);
|
||||
|
||||
List<SnapshotJoinVO> searchBySnapshotStorePair(String... pairs);
|
||||
List<SnapshotJoinVO> findByDistinctIds(Long zoneId, Long... ids);
|
||||
}
|
||||
@ -0,0 +1,248 @@
|
||||
// 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 com.cloud.api.query.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.annotation.AnnotationService;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
import org.apache.cloudstack.api.ResponseObject;
|
||||
import org.apache.cloudstack.api.response.SnapshotResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.query.QueryService;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.api.ApiResponseHelper;
|
||||
import com.cloud.api.query.vo.SnapshotJoinVO;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountService;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
public class SnapshotJoinDaoImpl extends GenericDaoBaseWithTagInformation<SnapshotJoinVO, SnapshotResponse> implements SnapshotJoinDao {
|
||||
|
||||
public static final Logger s_logger = Logger.getLogger(SnapshotJoinDaoImpl.class);
|
||||
|
||||
@Inject
|
||||
private AccountService accountService;
|
||||
@Inject
|
||||
private AnnotationDao annotationDao;
|
||||
@Inject
|
||||
private ConfigurationDao configDao;
|
||||
@Inject
|
||||
SnapshotDataFactory snapshotDataFactory;
|
||||
|
||||
private final SearchBuilder<SnapshotJoinVO> snapshotStorePairSearch;
|
||||
|
||||
private final SearchBuilder<SnapshotJoinVO> snapshotIdsSearch;
|
||||
|
||||
SnapshotJoinDaoImpl() {
|
||||
snapshotStorePairSearch = createSearchBuilder();
|
||||
snapshotStorePairSearch.and("snapshotStoreState", snapshotStorePairSearch.entity().getStoreState(), SearchCriteria.Op.IN);
|
||||
snapshotStorePairSearch.and("snapshotStoreIdIN", snapshotStorePairSearch.entity().getSnapshotStorePair(), SearchCriteria.Op.IN);
|
||||
snapshotStorePairSearch.done();
|
||||
|
||||
snapshotIdsSearch = createSearchBuilder();
|
||||
snapshotIdsSearch.and("zoneId", snapshotIdsSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
|
||||
snapshotIdsSearch.and("idsIN", snapshotIdsSearch.entity().getId(), SearchCriteria.Op.IN);
|
||||
snapshotIdsSearch.groupBy(snapshotIdsSearch.entity().getId());
|
||||
snapshotIdsSearch.done();
|
||||
}
|
||||
|
||||
private void setSnapshotInfoDetailsInResponse(SnapshotJoinVO snapshot, SnapshotResponse snapshotResponse, boolean isShowUnique) {
|
||||
if (!isShowUnique) {
|
||||
return;
|
||||
}
|
||||
if (snapshot.getDataCenterId() == null) {
|
||||
return;
|
||||
}
|
||||
SnapshotInfo snapshotInfo = null;
|
||||
snapshotInfo = snapshotDataFactory.getSnapshotWithRoleAndZone(snapshot.getId(), snapshot.getStoreRole(), snapshot.getDataCenterId());
|
||||
if (snapshotInfo == null) {
|
||||
s_logger.debug("Unable to find info for image store snapshot with uuid " + snapshot.getUuid());
|
||||
snapshotResponse.setRevertable(false);
|
||||
} else {
|
||||
snapshotResponse.setRevertable(snapshotInfo.isRevertable());
|
||||
snapshotResponse.setPhysicalSize(snapshotInfo.getPhysicalSize());
|
||||
}
|
||||
}
|
||||
|
||||
private String getSnapshotStatus(SnapshotJoinVO snapshot) {
|
||||
String status = snapshot.getStatus().toString();
|
||||
if (snapshot.getDownloadState() == null) {
|
||||
return status;
|
||||
}
|
||||
if (snapshot.getDownloadState() != VMTemplateStorageResourceAssoc.Status.DOWNLOADED) {
|
||||
status = "Processing";
|
||||
if (snapshot.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS) {
|
||||
status = snapshot.getDownloadPercent() + "% Downloaded";
|
||||
} else if (snapshot.getErrorString() == null) {
|
||||
status = snapshot.getStoreState().toString();
|
||||
} else {
|
||||
status = snapshot.getErrorString();
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotResponse newSnapshotResponse(ResponseObject.ResponseView view, boolean isShowUnique, SnapshotJoinVO snapshot) {
|
||||
final Account caller = CallContext.current().getCallingAccount();
|
||||
SnapshotResponse snapshotResponse = new SnapshotResponse();
|
||||
snapshotResponse.setId(snapshot.getUuid());
|
||||
// populate owner.
|
||||
ApiResponseHelper.populateOwner(snapshotResponse, snapshot);
|
||||
if (snapshot.getVolumeId() != null) {
|
||||
snapshotResponse.setVolumeId(snapshot.getVolumeUuid());
|
||||
snapshotResponse.setVolumeName(snapshot.getVolumeName());
|
||||
snapshotResponse.setVolumeType(snapshot.getVolumeType().name());
|
||||
snapshotResponse.setVirtualSize(snapshot.getVolumeSize());
|
||||
}
|
||||
snapshotResponse.setZoneId(snapshot.getDataCenterUuid());
|
||||
snapshotResponse.setZoneName(snapshot.getDataCenterName());
|
||||
snapshotResponse.setCreated(snapshot.getCreated());
|
||||
snapshotResponse.setName(snapshot.getName());
|
||||
String intervalType = null;
|
||||
if (snapshot.getSnapshotType() >= 0 && snapshot.getSnapshotType() < Snapshot.Type.values().length) {
|
||||
intervalType = Snapshot.Type.values()[snapshot.getSnapshotType()].name();
|
||||
}
|
||||
snapshotResponse.setIntervalType(intervalType);
|
||||
snapshotResponse.setState(snapshot.getStatus());
|
||||
snapshotResponse.setLocationType(snapshot.getLocationType() != null ? snapshot.getLocationType().name() : null);
|
||||
if (!isShowUnique) {
|
||||
snapshotResponse.setDatastoreState(snapshot.getStoreState() != null ? snapshot.getStoreState().name() : null);
|
||||
if (view.equals(ResponseObject.ResponseView.Full)) {
|
||||
snapshotResponse.setDatastoreId(snapshot.getStoreUuid());
|
||||
snapshotResponse.setDatastoreName(snapshot.getStoreName());
|
||||
snapshotResponse.setDatastoreType(snapshot.getStoreRole() != null ? snapshot.getStoreRole().name() : null);
|
||||
}
|
||||
// If the user is an 'Admin' or 'the owner of template' or template belongs to a project, add the template download status
|
||||
if (view == ResponseObject.ResponseView.Full ||
|
||||
snapshot.getAccountId() == caller.getId() ||
|
||||
snapshot.getAccountType() == Account.Type.PROJECT) {
|
||||
String status = getSnapshotStatus(snapshot);
|
||||
if (status != null) {
|
||||
snapshotResponse.setStatus(status);
|
||||
}
|
||||
}
|
||||
Map<String, String> downloadDetails = new HashMap<>();
|
||||
downloadDetails.put("downloadPercent", Integer.toString(snapshot.getDownloadPercent()));
|
||||
downloadDetails.put("downloadState", (snapshot.getDownloadState() != null ? snapshot.getDownloadState().toString() : ""));
|
||||
snapshotResponse.setDownloadDetails(downloadDetails);
|
||||
}
|
||||
setSnapshotInfoDetailsInResponse(snapshot, snapshotResponse, isShowUnique);
|
||||
setSnapshotResponse(snapshotResponse, snapshot);
|
||||
|
||||
snapshotResponse.setObjectName("snapshot");
|
||||
return snapshotResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SnapshotResponse setSnapshotResponse(SnapshotResponse snapshotResponse, SnapshotJoinVO snapshot) {
|
||||
// update tag information
|
||||
long tag_id = snapshot.getTagId();
|
||||
if (tag_id > 0) {
|
||||
addTagInformation(snapshot, snapshotResponse);
|
||||
}
|
||||
|
||||
if (snapshotResponse.hasAnnotation() == null) {
|
||||
snapshotResponse.setHasAnnotation(annotationDao.hasAnnotations(snapshot.getUuid(), AnnotationService.EntityType.SNAPSHOT.name(),
|
||||
accountService.isRootAdmin(CallContext.current().getCallingAccount().getId())));
|
||||
}
|
||||
return snapshotResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<SnapshotJoinVO>, Integer> searchIncludingRemovedAndCount(final SearchCriteria<SnapshotJoinVO> sc, final Filter filter) {
|
||||
List<SnapshotJoinVO> objects = searchIncludingRemoved(sc, filter, null, false);
|
||||
Integer count = getDistinctCount(sc);
|
||||
return new Pair<>(objects, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotJoinVO> searchBySnapshotStorePair(String... pairs) {
|
||||
// set detail batch query size
|
||||
int DETAILS_BATCH_SIZE = 2000;
|
||||
String batchCfg = configDao.getValue("detail.batch.query.size");
|
||||
if (batchCfg != null) {
|
||||
DETAILS_BATCH_SIZE = Integer.parseInt(batchCfg);
|
||||
}
|
||||
// query details by batches
|
||||
Filter searchFilter = new Filter(SnapshotJoinVO.class, "snapshotStorePair", QueryService.SortKeyAscending.value(), null, null);
|
||||
List<SnapshotJoinVO> uvList = new ArrayList<>();
|
||||
// query details by batches
|
||||
int curr_index = 0;
|
||||
if (pairs.length > DETAILS_BATCH_SIZE) {
|
||||
while ((curr_index + DETAILS_BATCH_SIZE) <= pairs.length) {
|
||||
String[] labels = new String[DETAILS_BATCH_SIZE];
|
||||
for (int k = 0, j = curr_index; j < curr_index + DETAILS_BATCH_SIZE; j++, k++) {
|
||||
labels[k] = pairs[j];
|
||||
}
|
||||
SearchCriteria<SnapshotJoinVO> sc = snapshotStorePairSearch.create();
|
||||
sc.setParameters("snapshotStoreIdIN", labels);
|
||||
List<SnapshotJoinVO> snaps = searchIncludingRemoved(sc, searchFilter, null, false);
|
||||
if (snaps != null) {
|
||||
uvList.addAll(snaps);
|
||||
}
|
||||
curr_index += DETAILS_BATCH_SIZE;
|
||||
}
|
||||
}
|
||||
if (curr_index < pairs.length) {
|
||||
int batch_size = (pairs.length - curr_index);
|
||||
String[] labels = new String[batch_size];
|
||||
for (int k = 0, j = curr_index; j < curr_index + batch_size; j++, k++) {
|
||||
labels[k] = pairs[j];
|
||||
}
|
||||
SearchCriteria<SnapshotJoinVO> sc = snapshotStorePairSearch.create();
|
||||
sc.setParameters("snapshotStoreIdIN", labels);
|
||||
List<SnapshotJoinVO> vms = searchIncludingRemoved(sc, searchFilter, null, false);
|
||||
if (vms != null) {
|
||||
uvList.addAll(vms);
|
||||
}
|
||||
}
|
||||
return uvList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SnapshotJoinVO> findByDistinctIds(Long zoneId, Long... ids) {
|
||||
if (ids == null || ids.length == 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Filter searchFilter = new Filter(SnapshotJoinVO.class, "snapshotStorePair", QueryService.SortKeyAscending.value(), null, null);
|
||||
|
||||
SearchCriteria<SnapshotJoinVO> sc = snapshotIdsSearch.create();
|
||||
if (zoneId != null) {
|
||||
sc.setParameters("zoneId", zoneId);
|
||||
}
|
||||
sc.setParameters("idsIN", ids);
|
||||
return searchIncludingRemoved(sc, searchFilter, null, false);
|
||||
}
|
||||
}
|
||||
352
server/src/main/java/com/cloud/api/query/vo/SnapshotJoinVO.java
Normal file
352
server/src/main/java/com/cloud/api/query/vo/SnapshotJoinVO.java
Normal file
@ -0,0 +1,352 @@
|
||||
// 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 com.cloud.api.query.vo;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
|
||||
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.storage.DataStoreRole;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
@Entity
|
||||
@Table(name = "snapshot_view")
|
||||
public class SnapshotJoinVO extends BaseViewWithTagInformationVO implements ControlledViewEntity {
|
||||
@Column(name = "uuid")
|
||||
private String uuid;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@Column(name = "status")
|
||||
@Enumerated(value = EnumType.STRING)
|
||||
private Snapshot.State status;
|
||||
|
||||
@Column(name = "disk_offering_id")
|
||||
Long diskOfferingId;
|
||||
|
||||
@Column(name = "snapshot_type")
|
||||
short snapshotType;
|
||||
|
||||
@Column(name = "type_description")
|
||||
String typeDescription;
|
||||
|
||||
@Column(name = "size")
|
||||
long size;
|
||||
|
||||
@Column(name = GenericDao.CREATED_COLUMN)
|
||||
Date created;
|
||||
|
||||
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||
Date removed;
|
||||
|
||||
@Column(name = "location_type")
|
||||
@Enumerated(value = EnumType.STRING)
|
||||
private Snapshot.LocationType locationType;
|
||||
|
||||
@Column(name = "hypervisor_type")
|
||||
@Enumerated(value = EnumType.STRING)
|
||||
Hypervisor.HypervisorType hypervisorType;
|
||||
|
||||
@Column(name = "account_id")
|
||||
private long accountId;
|
||||
|
||||
@Column(name = "account_uuid")
|
||||
private String accountUuid;
|
||||
|
||||
@Column(name = "account_name")
|
||||
private String accountName = null;
|
||||
|
||||
@Column(name = "account_type")
|
||||
@Enumerated(value = EnumType.ORDINAL)
|
||||
private Account.Type accountType;
|
||||
|
||||
@Column(name = "domain_id")
|
||||
private long domainId;
|
||||
|
||||
@Column(name = "domain_uuid")
|
||||
private String domainUuid;
|
||||
|
||||
@Column(name = "domain_name")
|
||||
private String domainName = null;
|
||||
|
||||
@Column(name = "domain_path")
|
||||
private String domainPath = null;
|
||||
|
||||
@Column(name = "project_id")
|
||||
private Long projectId;
|
||||
|
||||
@Column(name = "project_uuid")
|
||||
private String projectUuid;
|
||||
|
||||
@Column(name = "project_name")
|
||||
private String projectName;
|
||||
|
||||
@Column(name = "data_center_id")
|
||||
private Long dataCenterId;
|
||||
|
||||
@Column(name = "data_center_uuid")
|
||||
private String dataCenterUuid;
|
||||
|
||||
@Column(name = "data_center_name")
|
||||
private String dataCenterName;
|
||||
|
||||
@Column(name = "volume_id")
|
||||
private Long volumeId;
|
||||
|
||||
@Column(name = "volume_uuid")
|
||||
private String volumeUuid;
|
||||
|
||||
@Column(name = "volume_name")
|
||||
private String volumeName;
|
||||
|
||||
@Column(name = "volume_type")
|
||||
@Enumerated(EnumType.STRING)
|
||||
Volume.Type volumeType = Volume.Type.UNKNOWN;
|
||||
|
||||
@Column(name = "volume_size")
|
||||
Long volumeSize;
|
||||
|
||||
@Column(name = "store_id")
|
||||
private Long storeId;
|
||||
|
||||
@Column(name = "store_uuid")
|
||||
private String storeUuid;
|
||||
|
||||
@Column(name = "store_name")
|
||||
private String storeName;
|
||||
|
||||
@Column(name = "store_role")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private DataStoreRole storeRole;
|
||||
|
||||
@Column(name = "store_state")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ObjectInDataStoreStateMachine.State storeState;
|
||||
|
||||
@Column(name = "download_state")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private VMTemplateStorageResourceAssoc.Status downloadState;
|
||||
|
||||
@Column(name = "download_pct")
|
||||
private int downloadPercent;
|
||||
|
||||
@Column(name = "error_str")
|
||||
private String errorString;
|
||||
|
||||
@Column(name = "store_size")
|
||||
private long storeSize;
|
||||
|
||||
@Column(name = "created_on_store")
|
||||
private Date createdOnStore = null;
|
||||
|
||||
@Column(name = "snapshot_store_pair")
|
||||
private String snapshotStorePair;
|
||||
|
||||
@Override
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Snapshot.State getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public Long getDiskOfferingId() {
|
||||
return diskOfferingId;
|
||||
}
|
||||
|
||||
public short getSnapshotType() {
|
||||
return snapshotType;
|
||||
}
|
||||
|
||||
public String getTypeDescription() {
|
||||
return typeDescription;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public Date getRemoved() {
|
||||
return removed;
|
||||
}
|
||||
|
||||
public Snapshot.LocationType getLocationType() {
|
||||
return locationType;
|
||||
}
|
||||
|
||||
public Hypervisor.HypervisorType getHypervisorType() {
|
||||
return hypervisorType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccountUuid() {
|
||||
return accountUuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccountName() {
|
||||
return accountName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account.Type getAccountType() {
|
||||
return accountType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDomainId() {
|
||||
return domainId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDomainUuid() {
|
||||
return domainUuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDomainName() {
|
||||
return domainName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDomainPath() {
|
||||
return domainPath;
|
||||
}
|
||||
|
||||
public long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProjectUuid() {
|
||||
return projectUuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProjectName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
public Long getDataCenterId() {
|
||||
return dataCenterId;
|
||||
}
|
||||
|
||||
public String getDataCenterUuid() {
|
||||
return dataCenterUuid;
|
||||
}
|
||||
|
||||
public String getDataCenterName() {
|
||||
return dataCenterName;
|
||||
}
|
||||
|
||||
public Long getVolumeId() {
|
||||
return volumeId;
|
||||
}
|
||||
|
||||
public String getVolumeUuid() {
|
||||
return volumeUuid;
|
||||
}
|
||||
|
||||
public String getVolumeName() {
|
||||
return volumeName;
|
||||
}
|
||||
|
||||
public Volume.Type getVolumeType() {
|
||||
return volumeType;
|
||||
}
|
||||
|
||||
public Long getVolumeSize() {
|
||||
return volumeSize;
|
||||
}
|
||||
|
||||
public Long getStoreId() {
|
||||
return storeId;
|
||||
}
|
||||
|
||||
public String getStoreUuid() {
|
||||
return storeUuid;
|
||||
}
|
||||
|
||||
public String getStoreName() {
|
||||
return storeName;
|
||||
}
|
||||
|
||||
public DataStoreRole getStoreRole() {
|
||||
return storeRole;
|
||||
}
|
||||
|
||||
public ObjectInDataStoreStateMachine.State getStoreState() {
|
||||
return storeState;
|
||||
}
|
||||
|
||||
public VMTemplateStorageResourceAssoc.Status getDownloadState() {
|
||||
return downloadState;
|
||||
}
|
||||
|
||||
public int getDownloadPercent() {
|
||||
return downloadPercent;
|
||||
}
|
||||
|
||||
public String getErrorString() {
|
||||
return errorString;
|
||||
}
|
||||
|
||||
public long getStoreSize() {
|
||||
return storeSize;
|
||||
}
|
||||
|
||||
public Date getCreatedOnStore() {
|
||||
return createdOnStore;
|
||||
}
|
||||
|
||||
public String getSnapshotStorePair() {
|
||||
return snapshotStorePair;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getEntityType() {
|
||||
return Snapshot.class;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user