Skip to content

Commit 19bcfc3

Browse files
Locharla, SandeepLocharla, Sandeep
authored andcommitted
refactored few test cases
1 parent 98c9ba8 commit 19bcfc3

4 files changed

Lines changed: 77 additions & 36 deletions

File tree

test/integration/plugins/ontap/nfs3/instance/test_vm_volume_attach.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ def test_04_attach_volume_to_vm(self):
491491
(Note: on ONTAP/NFS shared storage the volume state remains 'Ready';
492492
attachment is signalled by virtualmachineid being populated)
493493
- ONTAP: FlexVol remains online
494+
- ONTAP: NFS3 volume data file created in FlexVol after attach (lazy creation)
494495
- VM remains 'Running'
495496
"""
496497
if self.__class__.vm is None:
@@ -539,6 +540,17 @@ def test_04_attach_volume_to_vm(self):
539540
"ONTAP FlexVol should be 'online' after attach"
540541
)
541542

543+
# ONTAP: NFS3 uses lazy file creation — the volume data file is
544+
# materialised on the FlexVol only when CloudStack calls createAsync
545+
# during attachVolume. Verify that the file now exists.
546+
files = self.ontap.list_files_in_volume(pool.name)
547+
vol_file = next((f for f in files if vol.id in f), None)
548+
self.assertIsNotNone(
549+
vol_file,
550+
"No data file matching volume UUID '%s' found in FlexVol '%s' "
551+
"after attach; files present: %s" % (vol.id, pool.name, files)
552+
)
553+
542554
# ------------------------------------------------------------------
543555
# Step 05 - Stop VM — export policy must be retained
544556
# ------------------------------------------------------------------
@@ -666,6 +678,7 @@ def test_07_detach_volume_from_vm(self):
666678
- Volume no longer lists the VM's ID
667679
- VM remains 'Running'
668680
- ONTAP: FlexVol remains online
681+
- ONTAP: NFS3 volume data file persists in FlexVol after detach
669682
"""
670683
if self.__class__.vm is None:
671684
self.skipTest("VM not deployed — test_03 was skipped (no ready template)")
@@ -723,6 +736,16 @@ def test_07_detach_volume_from_vm(self):
723736
"ONTAP FlexVol should be 'online' after detach"
724737
)
725738

739+
# ONTAP: NFS3 volume data file must still exist after detach — the file
740+
# is only removed when deleteVolume is called, not on detach.
741+
files = self.ontap.list_files_in_volume(pool.name)
742+
vol_file = next((f for f in files if vol.id in f), None)
743+
self.assertIsNotNone(
744+
vol_file,
745+
"Volume data file for '%s' should persist in FlexVol '%s' after "
746+
"detach; files present: %s" % (vol.id, pool.name, files)
747+
)
748+
726749
# ------------------------------------------------------------------
727750
# Step 08 - Destroy VM, delete volume, delete pool
728751
# ------------------------------------------------------------------
@@ -735,6 +758,7 @@ def test_08_destroy_vm_and_cleanup(self):
735758
Verifies:
736759
- VM is destroyed/expunged from CloudStack
737760
- Volume is deleted from CloudStack
761+
- ONTAP: NFS3 volume data file removed from FlexVol after deleteVolume
738762
- Pool is removed from CloudStack
739763
- ONTAP: FlexVol is deleted after pool removal
740764
- ONTAP: Export policy is removed after pool removal
@@ -766,11 +790,24 @@ def test_08_destroy_vm_and_cleanup(self):
766790

767791
# Delete the ONTAP data volume
768792
if vol is not None:
793+
vol_id = vol.id
769794
cmd = deleteVolumeAPI.deleteVolumeCmd()
770-
cmd.id = vol.id
795+
cmd.id = vol_id
771796
self.apiClient.deleteVolume(cmd)
772797
self.__class__.volume = None
773798

799+
# ONTAP: NFS3 volume data file must be removed from the FlexVol
800+
# after deleteVolume (CloudStack/libvirt deletes the file from the
801+
# NFS mount as part of the destroy workflow).
802+
files = self.ontap.list_files_in_volume(pool_name)
803+
vol_file = next((f for f in files if vol_id in f), None)
804+
self.assertIsNone(
805+
vol_file,
806+
"Volume data file for '%s' should be gone from FlexVol '%s' "
807+
"after deleteVolume; files still present: %s"
808+
% (vol_id, pool_name, files)
809+
)
810+
774811
# Enter maintenance then delete the pool
775812
maint_cmd = enableStorageMaintenance.enableStorageMaintenanceCmd()
776813
maint_cmd.id = pool.id

test/integration/plugins/ontap/nfs3/pool/test_pool_lifecycle.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
Running:
4141
nosetests --with-marvin \\
4242
--marvin-config=test/integration/plugins/ontap/ontap.cfg \\
43-
test/integration/plugins/ontap/test_ontap_create_primary_storage_nfs3.py -v
43+
test/integration/plugins/ontap/nfs3/pool/test_pool_lifecycle.py -v
4444
4545
Note: Tests 01-06 share class-level state (sequential). Running a single test
4646
with -m "test_NN" will invoke setUpClass but the guard assertion will fail
@@ -477,18 +477,6 @@ def test_05_cancel_maintenance_mode(self):
477477
"""
478478
Cancel maintenance mode and verify the pool returns to Up.
479479
480-
cancelStorageMaintenance sends ModifyStoragePoolCommand(add=True) to the
481-
KVM agent, which calls createStoragePool() with details that include
482-
nfsMountOptions=vers=3. The agent rebuilds the libvirt pool XML with the
483-
xmlns:fs namespace extension and mounts the NFS share with vers=3.
484-
485-
Fix confirmed — LibvirtStorageAdaptor now correctly handles the case
486-
where a stale-active libvirt pool entry lingers at the mount point after
487-
sp.destroy() during enter-maintenance. The fix:
488-
1. Detects a stale-active pool (isActive==1 but mountpoint -q fails)
489-
and destroys it before re-creating.
490-
2. Retries createNetfsStoragePool once after 5 s on LibvirtException.
491-
492480
Verifies:
493481
- CloudStack reports pool state Up
494482
- ONTAP: FlexVol is still online

test/integration/plugins/ontap/nfs3/pool/test_pool_with_volumes.py

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,6 @@
1818
"""
1919
NFS3 pool lifecycle tests with a CloudStack volume present throughout.
2020
21-
Covers the TDS (section 10) scenarios that require a data volume to already
22-
exist on the pool during pool state transitions:
23-
24-
TDS Approach-1 SN 11-12 — Disable pool WITH volumes
25-
TDS Approach-1 SN 15-16 — Enable pool WITH volumes
26-
TDS Approach-1 SN 19-20 — Enter maintenance WITH volumes
27-
TDS Approach-1 SN 21-22 — Cancel maintenance WITH volumes (fix confirmed)
28-
TDS Negative SN 5-6 — Delete pool that has volumes; forced=False rejected
29-
30-
Note: TDS SN 7-8 (force-delete NFS3 pool after manual volume deletion) is
31-
already covered by test_ontap_create_primary_storage_nfs3.py test_07/test_08.
32-
3321
Tests are numbered test_01 ... test_07 and must run in that order. Each step
3422
builds on the shared state established by the previous step.
3523
@@ -360,8 +348,7 @@ def test_01_create_pool_and_volume(self):
360348
@attr(tags=["nfs3_with_volumes"], required_hardware=True)
361349
def test_02_disable_pool_volume_survives(self):
362350
"""
363-
Disable the pool while a CloudStack data volume exists on it.
364-
Covers TDS Approach-1 SN 11 (iSCSI) and SN 12 (NFS3):
351+
Disable the pool while a CloudStack data volume exists on it:
365352
- Pool should no longer be available for scheduling new CS volumes
366353
- The existing CS volume should continue to exist (not deleted)
367354
- ONTAP: FlexVol remains online; export policy unchanged
@@ -412,8 +399,7 @@ def test_02_disable_pool_volume_survives(self):
412399
@attr(tags=["nfs3_with_volumes"], required_hardware=True)
413400
def test_03_enable_pool_volume_intact(self):
414401
"""
415-
Re-enable the pool while a CloudStack data volume exists on it.
416-
Covers TDS Approach-1 SN 15 (iSCSI) and SN 16 (NFS3):
402+
Re-enable the pool while a CloudStack data volume exists on it:
417403
- Pool state transitions back to Up
418404
- The existing CS volume is still accessible
419405
- ONTAP: FlexVol remains online; export policy unchanged
@@ -464,8 +450,7 @@ def test_03_enable_pool_volume_intact(self):
464450
@attr(tags=["nfs3_with_volumes"], required_hardware=True)
465451
def test_04_enter_maintenance_volume_present(self):
466452
"""
467-
Enter maintenance mode while a CloudStack data volume exists on the pool.
468-
Covers TDS Approach-1 SN 19 (iSCSI) and SN 20 (NFS3):
453+
Enter maintenance mode while a CloudStack data volume exists on the pool:
469454
- Pool transitions to Maintenance state
470455
- Existing CS volume remains in CloudStack
471456
- ONTAP: FlexVol stays online (maintenance is a CS-only state)
@@ -517,8 +502,7 @@ def test_04_enter_maintenance_volume_present(self):
517502
@attr(tags=["nfs3_with_volumes"], required_hardware=True)
518503
def test_05_cancel_maintenance_with_volume(self):
519504
"""
520-
Cancel maintenance mode while a CloudStack data volume exists on the pool.
521-
Covers TDS Approach-1 SN 21 (iSCSI) and SN 22 (NFS3):
505+
Cancel maintenance mode while a CloudStack data volume exists on the pool:
522506
- cancelStorageMaintenance succeeds (KVM/NFS3 fix confirmed)
523507
- Pool returns to Up state
524508
- Existing CS volume is still present in CloudStack
@@ -578,8 +562,7 @@ def test_06_forced_false_delete_rejected(self):
578562
"""
579563
Enter maintenance mode then attempt to delete the pool (forced=False)
580564
while a CloudStack volume still exists on it. The operation must be
581-
rejected.
582-
Covers TDS Negative Scenarios SN 5 (iSCSI) and SN 6 (NFS3):
565+
rejected:
583566
- CloudstackAPIException is raised with an appropriate error
584567
- Pool remains in Maintenance state
585568
- CS volume still exists

test/integration/plugins/ontap/ontap_test_base.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,39 @@ def list_lun_maps_for_volume(self, svm_name, vol_name):
197197
return [r for r in data.get("records", [])
198198
if r.get("lun", {}).get("name", "").startswith(prefix)]
199199

200+
# -- NFS file helpers ----------------------------------------------------
201+
202+
def list_files_in_volume(self, vol_name, path="/"):
203+
"""Return a list of file names at ``path`` inside the named FlexVol.
204+
205+
Uses the ONTAP REST file-system API:
206+
GET /api/storage/volumes/{uuid}/files/{url_encoded_path}
207+
208+
The path must appear in the URL (not as a query parameter). The root
209+
directory is represented as ``%2F``.
210+
211+
Returns an empty list if the volume does not exist, the path is empty,
212+
or the request fails.
213+
"""
214+
vol = self.get_volume(vol_name)
215+
if not vol:
216+
return []
217+
vol_uuid = vol.get("uuid", "")
218+
if not vol_uuid:
219+
return []
220+
# URL-encode the path component (/ → %2F) and embed it in the URL.
221+
from urllib.parse import quote
222+
encoded_path = quote(path, safe="")
223+
try:
224+
resp = self._get(
225+
"/storage/volumes/%s/files/%s" % (vol_uuid, encoded_path),
226+
params={"fields": "name,type", "max_records": "500"}
227+
)
228+
except Exception:
229+
return []
230+
return [r.get("name", "") for r in resp.get("records", [])
231+
if r.get("name") not in (".", "..")]
232+
200233

201234
# ---------------------------------------------------------------------------
202235
# Base test class

0 commit comments

Comments
 (0)