Skip to content

Commit 9c3c8fd

Browse files
CSTACK-18_1: recreated dev branch due to merge conflicts
1 parent eace4ee commit 9c3c8fd

File tree

9 files changed

+418
-5
lines changed

9 files changed

+418
-5
lines changed

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import com.cloud.storage.ScopeType;
3232
import com.cloud.storage.dao.VolumeDao;
3333
import com.cloud.storage.dao.VolumeDetailsDao;
34+
import com.cloud.storage.dao.SnapshotDetailsDao;
35+
import com.cloud.storage.dao.SnapshotDetailsVO;
3436
import com.cloud.utils.Pair;
3537
import com.cloud.utils.exception.CloudRuntimeException;
3638
import com.cloud.vm.VirtualMachine;
@@ -47,18 +49,22 @@
4749
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
4850
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
4951
import org.apache.cloudstack.storage.command.CommandResult;
52+
import org.apache.cloudstack.storage.command.CreateObjectAnswer;
5053
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
5154
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
5255
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
5356
import org.apache.cloudstack.storage.feign.model.Lun;
5457
import org.apache.cloudstack.storage.service.SANStrategy;
58+
import org.apache.cloudstack.storage.feign.model.FileInfo;
5559
import org.apache.cloudstack.storage.service.StorageStrategy;
5660
import org.apache.cloudstack.storage.service.UnifiedSANStrategy;
5761
import org.apache.cloudstack.storage.service.model.AccessGroup;
5862
import org.apache.cloudstack.storage.service.model.CloudStackVolume;
5963
import org.apache.cloudstack.storage.service.model.ProtocolType;
64+
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
6065
import org.apache.cloudstack.storage.utils.Constants;
6166
import org.apache.cloudstack.storage.utils.Utility;
67+
import org.apache.commons.lang3.StringUtils;
6268
import org.apache.logging.log4j.LogManager;
6369
import org.apache.logging.log4j.Logger;
6470

@@ -80,6 +86,8 @@ public class OntapPrimaryDatastoreDriver implements PrimaryDataStoreDriver {
8086
@Inject private VMInstanceDao vmDao;
8187
@Inject private VolumeDao volumeDao;
8288
@Inject private VolumeDetailsDao volumeDetailsDao;
89+
@Inject private SnapshotDetailsDao snapshotDetailsDao;
90+
8391
@Override
8492
public Map<String, String> getCapabilities() {
8593
s_logger.trace("OntapPrimaryDatastoreDriver: getCapabilities: Called");
@@ -88,6 +96,7 @@ public Map<String, String> getCapabilities() {
8896
// TODO Set it to false once we start supporting snapshot feature
8997
mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.FALSE.toString());
9098
mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString(), Boolean.FALSE.toString());
99+
91100
return mapCapabilities;
92101
}
93102

@@ -254,6 +263,7 @@ public void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallbac
254263

255264
@Override
256265
public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) {
266+
257267
}
258268

259269
@Override
@@ -525,6 +535,81 @@ public long getUsedIops(StoragePool storagePool) {
525535
@Override
526536
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {
527537

538+
CreateCmdResult result;
539+
540+
try {
541+
VolumeInfo volumeInfo = snapshot.getBaseVolume();
542+
543+
VolumeVO volumeVO = volumeDao.findById(volumeInfo.getId());
544+
if(volumeVO == null) {
545+
throw new CloudRuntimeException("takeSnapshot: VolumeVO not found for id: " + volumeInfo.getId());
546+
}
547+
548+
/** we are keeping file path at volumeVO.getPath() */
549+
550+
StoragePoolVO storagePool = storagePoolDao.findById(volumeVO.getPoolId());
551+
if(storagePool == null) {
552+
s_logger.error("takeSnapshot : Storage Pool not found for id: " + volumeVO.getPoolId());
553+
throw new CloudRuntimeException("takeSnapshot : Storage Pool not found for id: " + volumeVO.getPoolId());
554+
}
555+
Map<String, String> poolDetails = storagePoolDetailsDao.listDetailsKeyPairs(volumeVO.getPoolId());
556+
StorageStrategy storageStrategy = Utility.getStrategyByStoragePoolDetails(poolDetails);
557+
558+
Map<String, String> cloudStackVolumeRequestMap = new HashMap<>();
559+
cloudStackVolumeRequestMap.put(Constants.VOLUME_UUID, poolDetails.get(Constants.VOLUME_UUID));
560+
cloudStackVolumeRequestMap.put(Constants.FILE_PATH, volumeVO.getPath());
561+
CloudStackVolume cloudStackVolume = storageStrategy.getCloudStackVolume(cloudStackVolumeRequestMap);
562+
if (cloudStackVolume == null || cloudStackVolume.getFile() == null) {
563+
throw new CloudRuntimeException("takeSnapshot: Failed to get source file to take snapshot");
564+
}
565+
long capacityBytes = storagePool.getCapacityBytes();
566+
567+
long usedBytes = getUsedBytes(storagePool);
568+
long fileSize = cloudStackVolume.getFile().getSize();
569+
570+
usedBytes += fileSize;
571+
572+
if (usedBytes > capacityBytes) {
573+
throw new CloudRuntimeException("Insufficient space remains in this primary storage to take a snapshot");
574+
}
575+
576+
storagePool.setUsedBytes(usedBytes);
577+
578+
SnapshotObjectTO snapshotObjectTo = (SnapshotObjectTO)snapshot.getTO();
579+
580+
String fileSnapshotName = volumeInfo.getName() + "-" + snapshot.getUuid();
581+
582+
int maxSnapshotNameLength = 64;
583+
int trimRequired = fileSnapshotName.length() - maxSnapshotNameLength;
584+
585+
if (trimRequired > 0) {
586+
fileSnapshotName = StringUtils.left(volumeInfo.getName(), (volumeInfo.getName().length() - trimRequired)) + "-" + snapshot.getUuid();
587+
}
588+
589+
CloudStackVolume snapCloudStackVolumeRequest = snapshotCloudStackVolumeRequestByProtocol(poolDetails, volumeVO.getPath(), fileSnapshotName);
590+
CloudStackVolume cloneCloudStackVolume = storageStrategy.snapshotCloudStackVolume(snapCloudStackVolumeRequest);
591+
592+
updateSnapshotDetails(snapshot.getId(), volumeInfo.getId(), poolDetails.get(Constants.VOLUME_UUID), cloneCloudStackVolume.getFile().getPath(), volumeVO.getPoolId(), fileSize);
593+
594+
snapshotObjectTo.setPath(Constants.ONTAP_SNAP_ID +"="+cloneCloudStackVolume.getFile().getPath());
595+
596+
/** Update size for the storage-pool including snapshot size */
597+
storagePoolDao.update(volumeVO.getPoolId(), storagePool);
598+
599+
CreateObjectAnswer createObjectAnswer = new CreateObjectAnswer(snapshotObjectTo);
600+
601+
result = new CreateCmdResult(null, createObjectAnswer);
602+
603+
result.setResult(null);
604+
}
605+
catch (Exception ex) {
606+
s_logger.error("takeSnapshot: Failed due to ", ex);
607+
result = new CreateCmdResult(null, new CreateObjectAnswer(ex.toString()));
608+
609+
result.setResult(ex.toString());
610+
}
611+
612+
callback.complete(result);
528613
}
529614

530615
@Override
@@ -622,4 +707,87 @@ private CloudStackVolume createDeleteCloudStackVolumeRequest(StoragePool storage
622707
return cloudStackVolumeDeleteRequest;
623708

624709
}
710+
711+
712+
private CloudStackVolume getCloudStackVolumeRequestByProtocol(Map<String, String> details, String filePath) {
713+
CloudStackVolume cloudStackVolumeRequest = null;
714+
ProtocolType protocolType = null;
715+
String protocol = null;
716+
717+
try {
718+
protocol = details.get(Constants.PROTOCOL);
719+
protocolType = ProtocolType.valueOf(protocol);
720+
} catch (IllegalArgumentException e) {
721+
throw new CloudRuntimeException("getCloudStackVolumeRequestByProtocol: Protocol: "+ protocol +" is not valid");
722+
}
723+
switch (protocolType) {
724+
case NFS3:
725+
cloudStackVolumeRequest = new CloudStackVolume();
726+
FileInfo fileInfo = new FileInfo();
727+
fileInfo.setPath(filePath);
728+
cloudStackVolumeRequest.setFile(fileInfo);
729+
String volumeUuid = details.get(Constants.VOLUME_UUID);
730+
cloudStackVolumeRequest.setFlexVolumeUuid(volumeUuid);
731+
break;
732+
default:
733+
throw new CloudRuntimeException("createCloudStackVolumeRequestByProtocol: Unsupported protocol " + protocol);
734+
}
735+
return cloudStackVolumeRequest;
736+
}
737+
738+
private CloudStackVolume snapshotCloudStackVolumeRequestByProtocol(Map<String, String> details,
739+
String sourcePath,
740+
String destinationPath) {
741+
CloudStackVolume cloudStackVolumeRequest = null;
742+
ProtocolType protocolType = null;
743+
String protocol = null;
744+
745+
try {
746+
protocol = details.get(Constants.PROTOCOL);
747+
protocolType = ProtocolType.valueOf(protocol);
748+
} catch (IllegalArgumentException e) {
749+
throw new CloudRuntimeException("getCloudStackVolumeRequestByProtocol: Protocol: "+ protocol +" is not valid");
750+
}
751+
switch (protocolType) {
752+
case NFS3:
753+
cloudStackVolumeRequest = new CloudStackVolume();
754+
FileInfo fileInfo = new FileInfo();
755+
fileInfo.setPath(sourcePath);
756+
cloudStackVolumeRequest.setFile(fileInfo);
757+
String volumeUuid = details.get(Constants.VOLUME_UUID);
758+
cloudStackVolumeRequest.setFlexVolumeUuid(volumeUuid);
759+
cloudStackVolumeRequest.setDestinationPath(destinationPath);
760+
break;
761+
default:
762+
throw new CloudRuntimeException("createCloudStackVolumeRequestByProtocol: Unsupported protocol " + protocol);
763+
764+
}
765+
return cloudStackVolumeRequest;
766+
}
767+
768+
/**
769+
*
770+
* @param csSnapshotId: generated snapshot id from cloudstack
771+
* @param csVolumeId: Source CS volume id
772+
* @param ontapVolumeUuid: storage flexvolume id
773+
* @param ontapNewSnapshot: generated snapshot id from ONTAP
774+
* @param storagePoolId: primary storage pool id
775+
* @param ontapSnapSize: Size of snapshot CS volume(LUN/file)
776+
*/
777+
private void updateSnapshotDetails(long csSnapshotId, long csVolumeId, String ontapVolumeUuid, String ontapNewSnapshot, long storagePoolId, long ontapSnapSize) {
778+
SnapshotDetailsVO snapshotDetail = new SnapshotDetailsVO(csSnapshotId, Constants.SRC_CS_VOLUME_ID, String.valueOf(csVolumeId), false);
779+
snapshotDetailsDao.persist(snapshotDetail);
780+
781+
snapshotDetail = new SnapshotDetailsVO(csSnapshotId, Constants.BASE_ONTAP_FV_ID, String.valueOf(ontapVolumeUuid), false);
782+
snapshotDetailsDao.persist(snapshotDetail);
783+
784+
snapshotDetail = new SnapshotDetailsVO(csSnapshotId, Constants.ONTAP_SNAP_ID, String.valueOf(ontapNewSnapshot), false);
785+
snapshotDetailsDao.persist(snapshotDetail);
786+
787+
snapshotDetail = new SnapshotDetailsVO(csSnapshotId, Constants.PRIMARY_POOL_ID, String.valueOf(storagePoolId), false);
788+
snapshotDetailsDao.persist(snapshotDetail);
789+
790+
snapshotDetail = new SnapshotDetailsVO(csSnapshotId, Constants.ONTAP_SNAP_SIZE, String.valueOf(ontapSnapSize), false);
791+
snapshotDetailsDao.persist(snapshotDetail);
792+
}
625793
}

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/client/NASFeignClient.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121

2222
import feign.QueryMap;
2323
import org.apache.cloudstack.storage.feign.model.ExportPolicy;
24+
import org.apache.cloudstack.storage.feign.model.FileClone;
2425
import org.apache.cloudstack.storage.feign.model.FileInfo;
26+
import org.apache.cloudstack.storage.feign.model.response.JobResponse;
2527
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
2628
import feign.Headers;
2729
import feign.Param;
@@ -58,6 +60,11 @@ void createFile(@Param("authHeader") String authHeader,
5860
@Param("path") String filePath,
5961
FileInfo file);
6062

63+
@RequestLine("POST /api/storage/volumes/{volumeUuid}/files/{path}")
64+
@Headers({"Authorization: {authHeader}"})
65+
JobResponse cloneFile(@Param("authHeader") String authHeader,
66+
FileClone fileClone);
67+
6168
// Export Policy Operations
6269
@RequestLine("POST /api/protocols/nfs/export-policies")
6370
@Headers({"Authorization: {authHeader}"})
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.cloudstack.storage.feign.model;
21+
22+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
23+
import com.fasterxml.jackson.annotation.JsonInclude;
24+
import com.fasterxml.jackson.annotation.JsonProperty;
25+
26+
@JsonIgnoreProperties(ignoreUnknown = true)
27+
@JsonInclude(JsonInclude.Include.NON_NULL)
28+
public class FileClone {
29+
@JsonProperty("source_path")
30+
private String sourcePath;
31+
@JsonProperty("destination_path")
32+
private String destinationPath;
33+
@JsonProperty("volume")
34+
private VolumeConcise volume;
35+
public VolumeConcise getVolume() {
36+
return volume;
37+
}
38+
public void setVolume(VolumeConcise volume) {
39+
this.volume = volume;
40+
}
41+
public String getSourcePath() {
42+
return sourcePath;
43+
}
44+
public void setSourcePath(String sourcePath) {
45+
this.sourcePath = sourcePath;
46+
}
47+
public String getDestinationPath() {
48+
return destinationPath;
49+
}
50+
public void setDestinationPath(String destinationPath) {}
51+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.cloudstack.storage.feign.model;
21+
22+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
23+
import com.fasterxml.jackson.annotation.JsonInclude;
24+
import com.fasterxml.jackson.annotation.JsonProperty;
25+
26+
@JsonIgnoreProperties(ignoreUnknown = true)
27+
@JsonInclude(JsonInclude.Include.NON_NULL)
28+
public class VolumeConcise {
29+
@JsonProperty("uuid")
30+
private String uuid;
31+
@JsonProperty("name")
32+
private String name;
33+
public String getUuid() {
34+
return uuid;
35+
}
36+
public void setUuid(String uuid) {
37+
this.uuid = uuid;
38+
}
39+
public String getName() {
40+
return name;
41+
}
42+
public void setName(String name) {}
43+
}

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/StorageStrategy.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ public void deleteStorageVolume(Volume volume) {
318318
try {
319319
// TODO: Implement lun and file deletion, if any, before deleting the volume
320320
JobResponse jobResponse = volumeFeignClient.deleteVolume(authHeader, volume.getUuid());
321-
Boolean jobSucceeded = jobPollForSuccess(jobResponse.getJob().getUuid(), 10, 1);
321+
Boolean jobSucceeded = jobPollForSuccess(jobResponse.getJob().getUuid(),5, 1);
322322
if (!jobSucceeded) {
323323
s_logger.error("Volume deletion job failed for volume: " + volume.getName());
324324
throw new CloudRuntimeException("Volume deletion job failed for volume: " + volume.getName());
@@ -506,6 +506,19 @@ public String getNetworkInterface() {
506506
*/
507507
abstract public CloudStackVolume getCloudStackVolume(Map<String, String> cloudStackVolumeMap);
508508

509+
510+
/**
511+
* Method encapsulates the behavior based on the opted protocol in subclasses.
512+
* it is going to mimic
513+
* snapshotLun for iSCSI, FC protocols
514+
* snapshotFile for NFS3.0 and NFS4.1 protocols
515+
*
516+
*
517+
* @param cloudstackVolume the source CloudStack volume
518+
* @return the retrieved snapshot CloudStackVolume object
519+
*/
520+
public abstract CloudStackVolume snapshotCloudStackVolume(CloudStackVolume cloudstackVolume);
521+
509522
/**
510523
* Method encapsulates the behavior based on the opted protocol in subclasses
511524
* createiGroup for iSCSI and FC protocols
@@ -569,7 +582,7 @@ public String getNetworkInterface() {
569582
*/
570583
abstract public Map<String, String> getLogicalAccess(Map<String, String> values);
571584

572-
private Boolean jobPollForSuccess(String jobUUID, int maxRetries, int sleepTimeInSecs) {
585+
protected Boolean jobPollForSuccess(String jobUUID, int maxRetries, int sleepTimeInSecs) {
573586
//Create URI for GET Job API
574587
int jobRetryCount = 0;
575588
Job jobResp = null;

0 commit comments

Comments
 (0)