@@ -136,10 +136,17 @@ public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map<S
136136 // Get junction path from details map (set by VolumeOrchestrator as MOUNT_POINT)
137137 // This contains the ONTAP junction path like "/cs_vol_7e72cff5_9730_46e3_80ff_fb76fd7b1dc8"
138138 String junctionPath = details != null ? details .get (DiskTO .MOUNT_POINT ) : null ;
139- // Validate junction path
139+
140+ // Fallback: If MOUNT_POINT not in details, check if volumeUuid parameter itself is a junction path
141+ // (starts with /cs_vol_ or /) - this happens for ROOT volumes where vol.getPath() contains junction path
140142 if (junctionPath == null || junctionPath .isEmpty ()) {
141- logger .error ("Missing junction path (MOUNT_POINT) in details for volume: " + volumeUuid );
142- return false ;
143+ if (volumeUuid != null && volumeUuid .startsWith ("/" )) {
144+ logger .info ("volumeUuid parameter appears to be junction path: " + volumeUuid );
145+ junctionPath = volumeUuid ;
146+ } else {
147+ logger .error ("Missing junction path (MOUNT_POINT) in details and volumeUuid doesn't look like junction path: " + volumeUuid );
148+ return false ;
149+ }
143150 }
144151 logger .info ("Using junction path: " + junctionPath + " for volume: " + volumeUuid );
145152 // Create a sanitized mount point name (remove leading slash, replace special chars)
@@ -152,27 +159,27 @@ public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map<S
152159 logger .info ("Volume already mounted: " + mountPoint );
153160 return true ;
154161 }
155-
156162 try {
157163 // Create mount point directory
158164 _storageLayer .mkdirs (mountPoint );
159-
160165 // Mount: mount -t nfs <nfsServer>:<junctionPath> <mountPoint>
161166 String nfsServer = pool .getSourceHost ();
162167 String mountCmd = "mount -t nfs " + nfsServer + ":" + junctionPath + " " + mountPoint ;
163168 Script script = new Script ("/bin/bash" , 60000 , logger );
164169 script .add ("-c" );
165170 script .add (mountCmd );
166171 String result = script .execute ();
167-
168172 if (result != null ) {
169173 logger .error ("Failed to mount ONTAP NFS volume: " + volumeUuid + ", error: " + result );
170174 return false ;
171175 }
172-
173176 logger .info ("Successfully mounted ONTAP NFS volume: " + nfsServer + ":" + junctionPath + " at " + mountPoint );
174177 // Store the mapping so other methods can find the correct mount point
178+ // Store both volumeUuid and junctionPath as keys (in case they're different)
175179 volumeToMountPointMap .put (volumeUuid , mountPoint );
180+ if (!volumeUuid .equals (junctionPath )) {
181+ volumeToMountPointMap .put (junctionPath , mountPoint );
182+ }
176183 return true ;
177184 } catch (Exception e ) {
178185 logger .error ("Exception mounting ONTAP NFS volume: " + volumeUuid , e );
@@ -204,9 +211,7 @@ private String getMountPointForVolume(String volumeUuid) {
204211 @ Override
205212 public boolean disconnectPhysicalDisk (String volumeUuid , KVMStoragePool pool ) {
206213 logger .info ("Disconnecting ONTAP NFS volume: " + volumeUuid );
207-
208214 String mountPoint = getMountPointForVolume (volumeUuid );
209-
210215 if (!isMounted (mountPoint )) {
211216 logger .info ("Volume not mounted, nothing to disconnect: " + mountPoint );
212217 return true ;
@@ -238,18 +243,15 @@ public boolean disconnectPhysicalDisk(Map<String, String> volumeToDisconnect) {
238243 String volumeUuid = volumeToDisconnect .get (DiskTO .UUID );
239244 // Note: POOL_UUID constant doesn't exist in DiskTO, pool info comes from other sources
240245 String poolUuid = volumeToDisconnect .get ("poolUuid" );
241-
242246 if (volumeUuid == null || poolUuid == null ) {
243247 logger .error ("Missing volume UUID or pool UUID in disconnect request" );
244248 return false ;
245249 }
246-
247250 KVMStoragePool pool = getStoragePool (poolUuid );
248251 if (pool == null ) {
249252 logger .error ("Storage pool not found: " + poolUuid );
250253 return false ;
251254 }
252-
253255 return disconnectPhysicalDisk (volumeUuid , pool );
254256 }
255257
@@ -259,21 +261,16 @@ public boolean disconnectPhysicalDiskByPath(String localPath) {
259261 if (localPath == null || !localPath .startsWith (_mountPoint + "/" )) {
260262 return false ; // Not our path
261263 }
262-
263264 String [] pathParts = localPath .split ("/" );
264265 if (pathParts .length < 4 ) {
265266 return false ;
266267 }
267-
268268 String volumeUuid = pathParts [2 ]; // /mnt/<volumeUuid>/...
269269 String mountPoint = _mountPoint + "/" + volumeUuid ;
270-
271270 logger .info ("Disconnecting ONTAP NFS volume by path: " + localPath + " -> " + volumeUuid );
272-
273271 if (!isMounted (mountPoint )) {
274272 return true ;
275273 }
276-
277274 try {
278275 Script script = new Script ("/bin/bash" , 60000 , logger );
279276 script .add ("-c" );
@@ -297,7 +294,6 @@ public boolean disconnectPhysicalDiskByPath(String localPath) {
297294 @ Override
298295 public KVMPhysicalDisk getPhysicalDisk (String volumeUuid , KVMStoragePool pool ) {
299296 logger .debug ("Getting physical disk info for: " + volumeUuid );
300-
301297 // Path to qcow2 file: <mountPoint>/<volumeUuid>
302298 String mountPoint = getMountPointForVolume (volumeUuid );
303299 String diskPath = mountPoint + "/" + volumeUuid ;
@@ -312,7 +308,6 @@ public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool) {
312308 return disk ;
313309 }
314310 KVMPhysicalDisk disk = new KVMPhysicalDisk (diskPath , volumeUuid , pool );
315-
316311 try {
317312 // Get disk info using qemu-img info
318313 QemuImgFile imgFile = new QemuImgFile (diskPath );
@@ -325,19 +320,16 @@ public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool) {
325320 } else if ("raw" .equalsIgnoreCase (format )) {
326321 disk .setFormat (PhysicalDiskFormat .RAW );
327322 }
328-
329323 String sizeStr = info .get ("virtual_size" );
330324 if (sizeStr != null ) {
331325 disk .setVirtualSize (Long .parseLong (sizeStr ));
332326 disk .setSize (Long .parseLong (sizeStr ));
333327 }
334-
335328 } catch (QemuImgException | LibvirtException e ) {
336329 logger .warn ("Failed to get qemu-img info for: " + diskPath + ", " + e .getMessage ());
337330 // Set default format if qemu-img fails
338331 disk .setFormat (PhysicalDiskFormat .QCOW2 );
339332 }
340-
341333 return disk ;
342334 }
343335
@@ -374,11 +366,9 @@ public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, KVMStoragePool pool
374366 throw new CloudRuntimeException ("ONTAP volume not mounted for: " + volumeUuid +
375367 ". Volume must be mounted before creating disk." );
376368 }
377-
378369 // Create qcow2 file using qemu-img
379370 QemuImgFile destFile = new QemuImgFile (diskPath , size , format );
380371 QemuImg qemuImg = new QemuImg (0 );
381-
382372 Map <String , String > options = new HashMap <>();
383373 if (format == PhysicalDiskFormat .QCOW2 && provisioningType == Storage .ProvisioningType .THIN ) {
384374 options .put ("preallocation" , "metadata" );
@@ -387,19 +377,14 @@ public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, KVMStoragePool pool
387377 } else if (format == PhysicalDiskFormat .QCOW2 && provisioningType == Storage .ProvisioningType .FAT ) {
388378 options .put ("preallocation" , "falloc" );
389379 }
390-
391380 qemuImg .create (destFile , options );
392-
393381 logger .info ("Successfully created qcow2 file: " + diskPath );
394-
395382 // Return disk object
396383 KVMPhysicalDisk disk = new KVMPhysicalDisk (diskPath , volumeUuid , pool );
397384 disk .setFormat (format );
398385 disk .setSize (size );
399386 disk .setVirtualSize (size );
400-
401387 return disk ;
402-
403388 } catch (QemuImgException | LibvirtException e ) {
404389 logger .error ("Failed to create qcow2 file: " + diskPath , e );
405390 throw new CloudRuntimeException ("Failed to create ONTAP NFS disk: " + e .getMessage ());
@@ -415,32 +400,25 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, Phys
415400 @ Override
416401 public boolean deletePhysicalDisk (String volumeUuid , KVMStoragePool pool , Storage .ImageFormat format ) {
417402 logger .info ("Deleting ONTAP NFS physical disk: " + volumeUuid );
418-
419403 String mountPoint = getMountPointForVolume (volumeUuid );
420404 String diskPath = mountPoint + "/" + volumeUuid ;
421-
422405 try {
423406 // Delete qcow2 file
424407 if (_storageLayer .exists (diskPath )) {
425408 _storageLayer .delete (diskPath );
426409 logger .info ("Deleted qcow2 file: " + diskPath );
427410 }
428-
429411 // Unmount the volume
430412 if (isMounted (mountPoint )) {
431413 disconnectPhysicalDisk (volumeUuid , pool );
432414 }
433-
434415 // Remove mount point directory
435416 if (_storageLayer .exists (mountPoint )) {
436417 _storageLayer .delete (mountPoint );
437418 }
438-
439419 // Note: The ONTAP volume itself is deleted by OntapPrimaryDatastoreDriver via API
440-
441420 logger .info ("Successfully deleted ONTAP NFS disk: " + volumeUuid );
442421 return true ;
443-
444422 } catch (Exception e ) {
445423 logger .error ("Failed to delete ONTAP NFS disk: " + volumeUuid , e );
446424 return false ;
@@ -492,34 +470,27 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk srcDisk, String name, KV
492470 public KVMPhysicalDisk copyPhysicalDisk (KVMPhysicalDisk srcDisk , String name , KVMStoragePool destPool , int timeout ,
493471 byte [] srcPassphrase , byte [] dstPassphrase , Storage .ProvisioningType provisioningType ) {
494472 logger .info ("Copying disk: " + srcDisk .getName () + " -> " + name );
495-
496473 try {
497474 // Destination path: /mnt/<name>/<name>
498475 String destMountPoint = _mountPoint + "/" + name ;
499476 String destPath = destMountPoint + "/" + name ;
500-
501477 // Ensure destination volume is mounted
502478 if (!isMounted (destMountPoint )) {
503479 throw new CloudRuntimeException ("Destination ONTAP volume not mounted: " + name );
504480 }
505-
506481 // Use qemu-img convert for the copy
507482 QemuImgFile srcFile = new QemuImgFile (srcDisk .getPath ());
508483 QemuImgFile destFile = new QemuImgFile (destPath , srcDisk .getVirtualSize (), srcDisk .getFormat ());
509-
510484 QemuImg qemuImg = new QemuImg (timeout );
511485 // Note: QemuImg.convert doesn't take passphrase parameters in this version
512486 // For encrypted volumes, use options map instead
513487 qemuImg .convert (srcFile , destFile );
514-
515488 logger .info ("Successfully copied disk to: " + destPath );
516-
517489 // Return destination disk
518490 KVMPhysicalDisk destDisk = new KVMPhysicalDisk (destPath , name , destPool );
519491 destDisk .setFormat (srcDisk .getFormat ());
520492 destDisk .setSize (srcDisk .getSize ());
521493 destDisk .setVirtualSize (srcDisk .getVirtualSize ());
522-
523494 return destDisk ;
524495
525496 } catch (QemuImgException | LibvirtException e ) {
@@ -532,25 +503,18 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk srcDisk, String name, KV
532503 public KVMPhysicalDisk createTemplateFromDirectDownloadFile (String templateFilePath , String destTemplatePath ,
533504 KVMStoragePool destPool , Storage .ImageFormat format , int timeout ) {
534505 logger .info ("Creating template from direct download: " + templateFilePath + " -> " + destTemplatePath );
535-
536506 try {
537507 // Use qemu-img convert to create template
538508 QemuImgFile srcFile = new QemuImgFile (templateFilePath );
539509 QemuImgFile destFile = new QemuImgFile (destTemplatePath );
540-
541510 QemuImg qemuImg = new QemuImg (timeout );
542511 qemuImg .convert (srcFile , destFile );
543-
544512 logger .info ("Successfully created template: " + destTemplatePath );
545-
546513 // Get the template name from path
547514 String name = destTemplatePath .substring (destTemplatePath .lastIndexOf ("/" ) + 1 );
548-
549515 KVMPhysicalDisk disk = new KVMPhysicalDisk (destTemplatePath , name , destPool );
550516 disk .setFormat (PhysicalDiskFormat .QCOW2 );
551-
552517 return disk ;
553-
554518 } catch (QemuImgException | LibvirtException e ) {
555519 logger .error ("Failed to create template from direct download" , e );
556520 throw new CloudRuntimeException ("Failed to create template: " + e .getMessage ());
0 commit comments