Skip to content

Commit 0b8e507

Browse files
Srivastava, PiyushSrivastava, Piyush
authored andcommitted
true managed nfs 2
1 parent ced147d commit 0b8e507

File tree

2 files changed

+71
-74
lines changed

2 files changed

+71
-74
lines changed

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

Lines changed: 69 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
4040
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
4141
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
42-
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
4342
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
4443
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
4544
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
@@ -49,7 +48,6 @@
4948
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
5049
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
5150
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
52-
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
5351
import org.apache.cloudstack.storage.feign.model.OntapStorage;
5452
import org.apache.cloudstack.storage.provider.StorageProviderFactory;
5553
import org.apache.cloudstack.storage.service.StorageStrategy;
@@ -90,37 +88,7 @@ public DataTO getTO(DataObject data) {
9088
}
9189

9290
@Override
93-
public DataStoreTO getStoreTO(DataStore store) {
94-
// Load storage pool details from database (includes "mountpoint" added during pool creation)
95-
Map<String, String> poolDetails = storagePoolDetailsDao.listDetailsKeyPairs(store.getId());
96-
97-
// Set details on the store before creating PrimaryDataStoreTO
98-
// This ensures PrimaryDataStoreTO constructor gets the details from database
99-
PrimaryDataStore primaryStore = (PrimaryDataStore) store;
100-
if (poolDetails != null && !poolDetails.isEmpty()) {
101-
// Merge existing details (if any) with database details
102-
Map<String, String> existingDetails = primaryStore.getDetails();
103-
if (existingDetails == null) {
104-
primaryStore.setDetails(poolDetails);
105-
} else {
106-
// Merge: database details take precedence
107-
Map<String, String> mergedDetails = new HashMap<>(existingDetails);
108-
mergedDetails.putAll(poolDetails);
109-
primaryStore.setDetails(mergedDetails);
110-
}
111-
}
112-
113-
// Now create PrimaryDataStoreTO - it will get details from primaryStore.getDetails()
114-
PrimaryDataStoreTO storeTO = new PrimaryDataStoreTO(primaryStore);
115-
116-
s_logger.info("OntapPrimaryDatastoreDriver: getStoreTO: Created PrimaryDataStoreTO for pool: " + store.getName());
117-
s_logger.info(" Pool UUID: " + store.getUuid());
118-
s_logger.info(" Host Address: " + primaryStore.getHostAddress());
119-
s_logger.info(" Path: " + primaryStore.getPath());
120-
s_logger.info(" Port: " + primaryStore.getPort());
121-
s_logger.info(" Final details in storeTO: " + storeTO.getDetails());
122-
return storeTO;
123-
}
91+
public DataStoreTO getStoreTO(DataStore store) { return null; }
12492

12593
@Override
12694
public void createAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CreateCmdResult> callback) {
@@ -142,12 +110,6 @@ public void createAsync(DataStore dataStore, DataObject dataObject, AsyncComplet
142110
if (dataObject.getType() == DataObjectType.VOLUME) {
143111
path = createCloudStackVolumeForTypeVolume(dataStore, dataObject);
144112
createCmdResult = new CreateCmdResult(path, new Answer(null, true, null));
145-
} else if (dataObject.getType() == DataObjectType.TEMPLATE) {
146-
// For templates, return the UUID as the install path
147-
// This will be used as the filename for the qcow2 file on NFS
148-
path = dataObject.getUuid();
149-
s_logger.info("createAsync: Template [{}] will use UUID as install path: {}", ((TemplateInfo)dataObject).getName(), path);
150-
createCmdResult = new CreateCmdResult(path, new Answer(null, true, null));
151113
} else {
152114
errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync";
153115
s_logger.error(errMsg);
@@ -263,20 +225,23 @@ private String createCloudStackVolumeForTypeVolume(DataStore dataStore, DataObje
263225
/**
264226
* Creates Managed NFS Volume with ONTAP backing storage.
265227
*
266-
* Architecture: 1 CloudStack Storage Pool = 1 ONTAP Volume (shared by all volumes)
228+
* Architecture: 1 CloudStack Volume = 1 ONTAP Volume = 1 NFS Export
267229
*
268230
* Flow:
269-
* 1. createAsync() stores volume metadata and NFS mount point
270-
* 2. Volume attach triggers ManagedNfsStorageAdaptor.connectPhysicalDisk()
271-
* 3. KVM mounts: nfs://nfsServer/junctionPath to /mnt/volumeUuid
272-
* 4. Libvirt creates qcow2 file via storageVolCreateXML()
273-
* 5. File created at: /vol/ontap_volume/volumeUuid (on ONTAP)
231+
* 1. Create ONTAP FlexVolume via REST API with junction path /cloudstack_vol_<volumeUuid>
232+
* 2. ONTAP automatically creates NFS export for the junction path
233+
* 3. Store volume metadata in CloudStack DB
234+
* 4. Volume attach triggers OntapNfsStorageAdaptor.connectPhysicalDisk()
235+
* 5. KVM mounts: nfs://nfsServer/cloudstack_vol_<volumeUuid> to /mnt/volumeUuid
236+
* 6. qemu-img creates qcow2 file in the mounted directory
237+
* 7. File created at: /vol/cloudstack_vol_<volumeUuid>/<volumeUuid>.qcow2 (on ONTAP)
274238
*
275239
* Key Details:
276-
* - All volumes in same pool share the same ONTAP volume NFS export
277-
* - Each volume gets separate libvirt mount point: /mnt/<volumeUuid>
278-
* - All qcow2 files stored in same ONTAP volume: /vol/<pool_volume_name>/
279-
* - volume._iScsiName stores the NFS junction path (pool.path)
240+
* - Each CloudStack volume gets its own dedicated ONTAP FlexVolume
241+
* - Each ONTAP volume has unique junction path: /cloudstack_vol_<volumeUuid>
242+
* - Each volume mounted at: /mnt/<volumeUuid>
243+
* - qcow2 file stored at root of ONTAP volume
244+
* - volume._iScsiName stores the NFS junction path for OntapNfsStorageAdaptor
280245
*
281246
* @param dataStore CloudStack data store (storage pool)
282247
* @param dataObject Volume data object
@@ -288,31 +253,52 @@ private String createManagedNfsVolume(DataStore dataStore, DataObject dataObject
288253
VolumeVO volume = volumeDao.findById(volumeInfo.getId());
289254
String volumeUuid = volumeInfo.getUuid();
290255

291-
// Get the NFS junction path from storage pool
292-
// This is the path that was set during pool creation (e.g., "/my_pool_volume")
293-
String junctionPath = storagePool.getPath();
256+
// Step 1: Create ONTAP FlexVolume via REST API
257+
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(dataStore.getId());
258+
StorageStrategy storageStrategy = getStrategyByStoragePoolDetails(details);
259+
260+
String ontapVolumeName = "cloudstack_vol_" + volumeUuid;
261+
String junctionPath = "/" + ontapVolumeName;
262+
long sizeInBytes = volumeInfo.getSize();
263+
264+
s_logger.info("Creating ONTAP FlexVolume: name={}, junctionPath={}, size={}GB for CloudStack volume {}",
265+
ontapVolumeName, junctionPath, sizeInBytes / (1024 * 1024 * 1024), volumeUuid);
294266

295-
// Update volume metadata in CloudStack database
296-
// Use OntapNFS pool type - matches pool type set during pool creation
267+
try {
268+
// Create ONTAP volume with NFS export
269+
org.apache.cloudstack.storage.feign.model.Volume ontapVolume =
270+
storageStrategy.createStorageVolume(ontapVolumeName, sizeInBytes);
271+
272+
if (ontapVolume == null || ontapVolume.getUuid() == null) {
273+
String errMsg = "Failed to create ONTAP volume: " + ontapVolumeName;
274+
s_logger.error(errMsg);
275+
throw new CloudRuntimeException(errMsg);
276+
}
277+
278+
s_logger.info("ONTAP FlexVolume created successfully: name={}, uuid={}, junctionPath={}",
279+
ontapVolumeName, ontapVolume.getUuid(), junctionPath);
280+
281+
} catch (Exception e) {
282+
String errMsg = "Exception creating ONTAP volume " + ontapVolumeName + ": " + e.getMessage();
283+
s_logger.error(errMsg, e);
284+
throw new CloudRuntimeException(errMsg, e);
285+
}
286+
287+
// Step 2: Update CloudStack volume metadata
297288
volume.setPoolType(Storage.StoragePoolType.OntapNFS);
298289
volume.setPoolId(dataStore.getId());
299290
volume.setPath(volumeUuid); // Filename for qcow2 file
300291

301-
// For OntapNFS, _iScsiName stores the per-volume junction path
302-
// OntapNfsStorageAdaptor will use this to mount the specific ONTAP volume
303-
// Format: /cloudstack_vol_<volumeUuid>
304-
volume.set_iScsiName("/cloudstack_vol_" + volumeUuid);
292+
// Store junction path in _iScsiName field
293+
// OntapNfsStorageAdaptor will use this to mount: nfs://<server>/cloudstack_vol_<volumeUuid>
294+
volume.set_iScsiName(junctionPath);
305295

306296
volumeDao.update(volume.getId(), volume);
307297

308-
s_logger.info("ONTAP Managed NFS Volume Created: uuid={}, path={}, junctionPath={}, format=QCOW2, " +
309-
"pool={}, size={}GB. Libvirt will create qcow2 file at mount time.",
310-
volumeUuid, volumeUuid, junctionPath, storagePool.getName(),
298+
s_logger.info("CloudStack volume metadata updated: uuid={}, path={}, junctionPath={}, poolType={}, size={}GB",
299+
volumeUuid, volumeUuid, junctionPath, "OntapNFS",
311300
volumeInfo.getSize() / (1024 * 1024 * 1024));
312301

313-
// Optional: Prepare ONTAP volume for optimal qcow2 storage (future enhancement)
314-
// prepareOntapVolumeForQcow2Storage(dataStore, volumeInfo);
315-
316302
return volumeUuid;
317303
}
318304

@@ -379,15 +365,31 @@ public void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallbac
379365
throw new CloudRuntimeException("deleteAsync: store or data is null");
380366
}
381367
if (data.getType() == DataObjectType.VOLUME) {
382-
StoragePoolVO storagePool = storagePoolDao.findById(store.getId());
383368
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(store.getId());
384369
if (ProtocolType.NFS.name().equalsIgnoreCase(details.get(Constants.PROTOCOL))) {
385-
// ManagedNFS qcow2 backing file deletion handled by KVM host/libvirt; nothing to do via ONTAP REST.
386-
s_logger.info("deleteAsync: ManagedNFS volume {} no-op ONTAP deletion", data.getId());
370+
VolumeInfo volumeInfo = (VolumeInfo) data;
371+
String volumeUuid = volumeInfo.getUuid();
372+
String ontapVolumeName = "cloudstack_vol_" + volumeUuid;
373+
374+
s_logger.info("deleteAsync: Deleting ONTAP FlexVolume {} for CloudStack volume {}",
375+
ontapVolumeName, volumeUuid);
376+
377+
// TODO: Implement ONTAP volume deletion via StorageStrategy.deleteStorageVolume()
378+
// For now, ONTAP volumes will remain after CloudStack volume deletion
379+
// This allows manual cleanup and prevents accidental data loss during development
380+
// Future implementation:
381+
// StorageStrategy storageStrategy = getStrategyByStoragePoolDetails(details);
382+
// Volume ontapVolume = new Volume();
383+
// ontapVolume.setName(ontapVolumeName);
384+
// storageStrategy.deleteStorageVolume(ontapVolume);
385+
386+
s_logger.warn("deleteAsync: ONTAP volume deletion not yet implemented. " +
387+
"Manual cleanup required for ONTAP volume: {}", ontapVolumeName);
387388
}
388389
}
389390
} catch (Exception e) {
390391
commandResult.setResult(e.getMessage());
392+
s_logger.error("deleteAsync: Exception deleting volume", e);
391393
} finally {
392394
callback.complete(commandResult);
393395
}

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/lifecycle/OntapPrimaryDatastoreLifecycle.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,6 @@ public DataStore initialize(Map<String, Object> dsInfos) {
188188

189189
// Determine storage pool type and path based on protocol
190190
String path;
191-
String host = "";
192191
ProtocolType protocol = ProtocolType.valueOf(details.get(Constants.PROTOCOL));
193192
switch (protocol) {
194193
case NFS:
@@ -203,7 +202,8 @@ public DataStore initialize(Map<String, Object> dsInfos) {
203202
// CloudStack will construct the full mount path as: hostAddress + ":" + path
204203
path = "/" + storagePoolName;
205204
s_logger.info("Setting NFS path for storage pool: " + path);
206-
host = "10.193.192.136"; // TODO hardcoded for now
205+
String host = "10.193.192.136"; //
206+
parameters.setHost(host);// TODO hardcoded for now
207207
break;
208208
case ISCSI:
209209
parameters.setType(Storage.StoragePoolType.Iscsi);
@@ -249,13 +249,8 @@ public DataStore initialize(Map<String, Object> dsInfos) {
249249
throw new CloudRuntimeException("ONTAP details validation failed, cannot create primary storage");
250250
}
251251

252-
// Add mountpoint detail for ManagedNFS - required by KVM agent's ManagedNfsStorageAdaptor
253-
// The 'mountpoint' key is used by connectPhysicalDisk() to mount NFS export
254-
details.put("mountpoint", path);
255-
256252
// Set parameters for primary data store
257253
parameters.setPort(Constants.ONTAP_PORT);
258-
parameters.setHost(host);
259254
parameters.setPath(path);
260255
parameters.setTags(tags != null ? tags : "");
261256
parameters.setIsTagARule(isTagARule != null ? isTagARule : Boolean.FALSE);

0 commit comments

Comments
 (0)